21.4 追加各个画面的类文件及实际代码

21.4.1 追加准妈妈生日输入画面的类及代码

首先追加准妈妈生日输入画面(见图21.1)的类。将鼠标指针移动到Groups&Files下的Classes目录上(目的是使新创建的类自动收容于Classes目录下),然后选择File→New File命令,在打开的窗口中选择UIViewController subclass项目,然后单击Next按钮(见图21.10)。进入下一画面后,在File Name文本框中将新类命名为WifeBirthdayController(见图21.11),然后单击Finish按钮,完成创建工作。

UIViewController是开发多画面iPhone应用程序时经常要接触到的类,UIViewController类提供了控制画面迁移的功能,主要功能有通过屏幕上方的控制条(Control Bar)来控制前后画面间的迁移。用户可以自由地设置控制条的外观以及控制按钮的分布等。关于UIViewController类的用法,本节将结合具体的代码进行进一步的解说。

图21.10 选择模板

图21.11 定义类名

分别编辑Classes目录中新增加的WifeBirthdayController.h及WifeBirthdayController.m文件,具体的代码如下。

Source WifeBirthdayController.h
#import <UIKit/UIKit.h>

@interface WifeBirthdayController : UIViewController { @private UIDatePicker* datePicker_; }
@end
Source WifeBirthdayController.m #import "WifeBirthdayController.h"
@implementation WifeBirthdayController
- (void)dealloc { [datePicker_ release]; [super dealloc]; } - (void)viewDidLoad { [super viewDidLoad]; //设置画面标题、说明信息 self.title = @"准妈妈的生日"; UILabel* label = [[[UILabel alloc] initWithFrame:CGRectMake ( 10, 10, 300, 50 )] autorelease]; NSString* longText =@"请选择准妈妈的生日。"; label.text = longText; label.numberOfLines = 1; label.backgroundColor = [UIColor blackColor]; label.textColor = [UIColor whiteColor]; //将创建说明标签对象追加到画面view中 [self.view addSubview:label]; //创建并追加准妈妈生日输入控件 datePicker_ = [[UIDatePicker alloc] init]; datePicker_.datePickerMode = UIDatePickerModeDate; CGPoint pickerCenterPoint = CGPointMake(150, 180); datePicker_.center = pickerCenterPoint; [self.view addSubview:datePicker_]; //追加下方的“下一步”按钮 UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:@"下一步" forState:UIControlStateNormal]; [button sizeToFit]; CGPoint newPoint = self.view.center; newPoint.y += 80; button.center = newPoint; //设置点击按钮时的事件响应方法 [button addTarget:self action:@selector(buttonDidPush) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; //向导航条(NavigationBar)中追加右按钮(rightBarButtonItem),按钮标题也为“下一步” UIBarButtonItem* rbutton = [[[UIBarButtonItem alloc] initWithTitle:@"下一步" style:UIBarButtonItemStyleDone target:self action:@selector(buttonDidPush)] autorelease]; self.navigationItem.rightBarButtonItem = rbutton; } //定义点击“下一步”按钮的响应方法buttonDidPush - (void)buttonDidPush { //取得输入的时期数据并输出到履历中 NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease]; [formatter setDateFormat:@"yyyy"]; // NSDateFormatter类用于取得指定格式的时间 NSString* yearStr = [formatter stringFromDate:datePicker_.date]; NSLog( @"selected birthday is %@", yearStr ); }

为了使应用程序GuessChild启动后能首先显示上述的准妈妈生日输入画面,我们还必须修改GuessChildAppDelegate.h 以及GuessChildAppDelegate.m文件。对它们分别进行如下修改。

修改 GuessChildAppDelegate.h 文件,追加 UINavigationController类型的变量simple_。

Source GuessChildAppDelegate.h
@interface GuessChildAppDelegate:NSObject <UIApplicationDelegate> {
   UIWindow *window;
   UINavigationController* simple_;
}

修改GuessChildAppDelegate.m文件,显示准妈妈生日输入画面。首先必须将WifeBirthdayController.h文件包含进来。接着在application:didFinishLaunchingWithOptions:方法中追加准妈妈生日输入画面初始化等代码。

Source GuessChildAppDelegate.m
...
//初始化准妈妈生日输入画面WifeBirthdayController
WifeBirthdayController* rootViewController = [[[WifeBirthdayController alloc]
init] autorelease];
simple_ = [[UINavigationController alloc] initWithRootViewController
:rootViewController];
[window addSubview:simple_.view];
// Override point for customization after application launch.
…

到此你可以编译并试运行GuessChild,注意选择“iPhone Simulator 4.x”(后面的21.5节中会有介绍)。此时可以显示如图21.1所示的准妈妈生日输入画面。单击按钮后,可以在控制台(Console)中看到所选择的日期的履历(Log)。

接着追加下一个画面,并追加画面迁移的相关代码。

21.4.2 追加准爸爸生日输入画面的类及代码

按照上述追加准妈妈生日输入画面时的方法,追加准爸爸生日输入画面的类,并将其命名为“HusbandBirthdayController”。接着编辑 HusbandBirthdayController.h及HusbandBirthdayController.m 文件,具体的代码如下。

source HusbandBirthdayController.h
#import <UIKit/UIKit.h>

@interface HusbandBirthdayController : UIViewController { @private UIDatePicker* datePicker_; //定义属性保存从上一画面传入的准妈妈生日 NSDate *wifeBirthday_; } @property (nonatomic, retain) NSDate *wifeBirthday; @end
Source HusbandBirthdayController.m #import "HusbandBirthdayController.h" #import "PregnantDayController.h"
@implementation HusbandBirthdayController
@synthesize wifeBirthday = wifeBirthday_; //资源释放方法 - (void)dealloc { [datePicker_ release]; [wifeBirthday_ release]; [super dealloc]; } - (void)viewDidLoad { [super viewDidLoad]; //追加画面标题,说明标签 self.title = @"准爸爸的生日"; UILabel* label = [[[UILabel alloc] initWithFrame:CGRectMake( 10,10, 300, 50 )] autorelease]; NSString* longText =@"请选择准爸爸的生日。"; label.text = longText; label.numberOfLines = 1; label.backgroundColor = [UIColor blackColor]; label.textColor = [UIColor whiteColor]; [self.view addSubview:label]; //追加准爸爸生日输入空间 datePicker_ = [[UIDatePicker alloc] init]; datePicker_.datePickerMode = UIDatePickerModeDate; CGPoint pickerCenterPoint = CGPointMake(150, 180); datePicker_.center = pickerCenterPoint; [self.view addSubview:datePicker_]; //追加画面下方以及导航条中的按钮 UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:@"下一步" forState:UIControlStateNormal]; [button sizeToFit]; CGPoint newPoint = self.view.center; newPoint.y += 80; button.center = newPoint; [button addTarget:self action:@selector(buttonDidPush) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button];
UIBarButtonItem* rbutton = [[[UIBarButtonItem alloc] initWithTitle:@"下一步" style:UIBarButtonItemStyleDone target:self action:@selector(buttonDidPush) ] autorelease]; self.navigationItem.rightBarButtonItem = rbutton; } //点击“下一步”按钮时的处理方法 - (void)buttonDidPush { NSDateFormatter* formatter=[[[NSDateFormatter alloc] init] autorelease]; [formatter setDateFormat:@"yyyy "]; //取得准爸爸的出生年 NSString* yearStr = [formatter stringFromDate:datePicker_.date]; //NSLog( @"selected birthday is %@", yearStr); int year = [yearStr intValue]; //当输入的生日为1994年前的生日时,显示错误信息 //此处目的为演示日期检查方法,至于日期检查内容是否妥当则别论 if (year > 1994) { UIAlertView* alert = [[[UIAlertView alloc] init] autorelease]; alert.message = @"日期不对! 必须输入一个成年人的生日。"; [alert addButtonWithTitle:@"OK"]; [alert show]; }else { //当日期合法时,准备转移到下一画面 //初始化下一画面(怀孕日期输入)的controller类 PregnantDayController *viewController = [[PregnantDayController alloc] init]; //将本画面(准爸爸生日)的日期保存相应属性中,同时别忘了保存第一画面的输入值 //iPhone 应用程序中没有类似Session的概念,只能这样传递已输入的内容 viewController.husbandBirthday = datePicker_.date; viewController.wifeBirthday = self.wifeBirthday; //调用pushViewController: animated:显示下一画面(怀孕日期输入) [self.navigationController pushViewController:viewController animated:YES]; } } @end

为了能在单击“下一步”按钮后,由前一个画面(准妈妈生日输入画面)迁移本画面(准爸爸生日输入画面),还必须修改WifeBirthdayController.m文件。

首先追加导入头文件 HusbandBirthdayController.h的代码,否则后面无法初始化HusbandBirthdayController类。

#import "HusbandBirthdayController.h"

接着需要修改“下一步”按钮的事件处理函数(buttonDidPush)。

-(void)buttonDidPush {
  ...
  int year = [yearStr intValue];
  if (year > 1994) {
      UIAlertView* alert = [[[UIAlertView alloc] init] autorelease];
      alert.message = @"日期不对! 必须输入一个成年人的生日。";
      [alert addButtonWithTitle:@"OK"];
      [alert show];
}else {
    HusbandBirthdayController *viewController = [[HusbandBirthday
    Controller alloc] init] ;
       //保存输入准妈妈的生日
       viewController.wifeBirthday = datePicker_.date;
       //调用pushViewController: animated:显示下一画面(准爸爸生日输入)
       [self.navigationController pushViewController:viewController
       animated:YES];
    }
}

21.4.3 追加怀孕日期输入画面的类及代码

按照上述追加准妈妈生日输入画面时的方法,追加怀孕日期输入画面的类,并将其命名为“PregnantDayController”。接着编辑 PregnantDayController.h及PregnantDayController.m 文件,具体的代码如下。

      Source PregnantDayController.h
      #import <UIKit/UIKit.h>

@interface PregnantDayController:UIViewController { @private UIDatePicker* datePicker_; NSDate *wifeBirthday_; NSDate *husbandBirthday_; } @property (nonatomic, retain) NSDate *wifeBirthday; @property (nonatomic, retain) NSDate *husbandBirthday; @end
Source PregnantDayController.m #import "PregnantDayController.h" #import "ResultController.h"
@implementation PregnantDayController
@synthesize wifeBirthday = wifeBirthday_; @synthesize husbandBirthday = husbandBirthday_; //资源释放方法 - (void)dealloc { [datePicker_ release]; [wifeBirthday_ release]; [husbandBirthday_ release]; [super dealloc]; } - (void)viewDidLoad { [super viewDidLoad]; //设置画面标题、信息标签 self.title = @"怀孕日期"; UILabel* label = [[[UILabel alloc] initWithFrame:CGRectMake( 10, 10, 300, 50 )] autorelease]; NSString* longText =@"请选择怀孕的日期。"; label.text = longText; label.numberOfLines = 2; label.backgroundColor = [UIColor blackColor]; label.textColor = [UIColor whiteColor]; [self.view addSubview:label]; //怀孕日期控件 datePicker_ = [[UIDatePicker alloc] init]; datePicker_.datePickerMode = UIDatePickerModeDate; CGPoint pickerCenterPoint = CGPointMake(150, 180); datePicker_.center = pickerCenterPoint; [self.view addSubview:datePicker_]; //在画面下方及导航条中追加“下一步”按钮 UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:@"下一步" forState:UIControlStateNormal]; [button sizeToFit]; CGPoint newPoint = self.view.center; newPoint.y += 80; button.center = newPoint; [button addTarget:self action:@selector(buttonDidPush) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; UIBarButtonItem* rbutton = [[[UIBarButtonItem alloc] initWithTitle:@"下一步" style:UIBarButtonItemStyleDone target:self action:@selector(buttonDidPush) ] autorelease]; self.navigationItem.rightBarButtonItem = rbutton; } //单击“下一步”按钮的处理 - (void)buttonDidPush { NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease]; [formatter setDateFormat:@"yyyy "]; NSString* yearStr = [formatter stringFromDate:datePicker_.date]; //NSLog( @"selected birthday is %@", yearStr ); int year = [yearStr intValue]; NSString* curyearStr = [formatter stringFromDate:[NSDate date]]; //NSLog( @"current year is %@", curyearStr ); int curyear = [curyearStr intValue]; //当怀孕日期为过去一年前的日期时,显示错误信息 if (year < curyear-1) { UIAlertView* alert = [[[UIAlertView alloc] init] autorelease]; alert.message = @"日期不合法! 怀孕日期必须是一个未来或者过去一年内的日期。"; [alert addButtonWithTitle:@"OK"]; [alert show]; }else { //初始化预测结果显示画面,并保存前后三个画面中的输入日期 ResultController *viewController = [[ResultController alloc] init]; viewController.pregnantDay = datePicker_.date; viewController.wifeBirthday = self.wifeBirthday; viewController.husbandBirthday = self.husbandBirthday; //调用pushViewController: animated:显示下一画面(预测结果) [self.navigationController pushViewController:viewController animated:YES]; } } @end

21.4.4 追加预测结果画面的类及代码

按照上述追加准妈妈生日输入画面时的方法,追加预测结果画面的类,并将其命名为“ResultController”。接着编辑 ResultController.h及 ResultController.m 文件,具体的代码如下。

Source ResultController.h
#import <UIKit/UIKit.h>

@interface ResultController : UIViewController { //定义保存输入日期的属性 NSDate *wifeBirthday_; NSDate *husbandBirthday_; NSDate *pregnantDay_; @private //结果预测画面设计了一个动画效果,定义保存动画图像的属性 UIImageView* imageView_; } @property (nonatomic, retain) NSDate *wifeBirthday; @property (nonatomic, retain) NSDate *husbandBirthday; @property (nonatomic, retain) NSDate *pregnantDay; @end
Source ResultController.m #import "ResultController.h" #import "WifeBirthdayController.h"
@implementation ResultController
@synthesize wifeBirthday = wifeBirthday_; @synthesize husbandBirthday = husbandBirthday_; @synthesize pregnantDay = pregnantDay_; //资源释放 - (void)dealloc { [wifeBirthday_ release]; [husbandBirthday_ release]; [pregnantDay_ release]; [imageView_ release]; [super dealloc]; } //画面显示后处理方法。画面显示后开始动画处理 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [imageView_ startAnimating]; } //画面关闭后处理方法。画面关闭后停止动画处理 - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [imageView_ stopAnimating]; } - (void)viewDidLoad { [super viewDidLoad]; //调用[getOldFirstDate: compareDate:]方法计算怀孕时准妈妈、准爸爸的年龄 int wifeOld = [self getOldFirstDate:self.wifeBirthday compareDate:self. pregnantDay]; int husbandOld = [self getOldFirstDate:self.husbandBirthday compareDate:self. pregnantDay]; //计算年龄和除以3后的余数 int remainder = (wifeOld +husbandOld) % 3; NSString *sex = @"女孩"; //余数为1时宝宝为男孩,否则为女孩 if (remainder == 1) { sex = @"男孩"; } //显示预测结果 NSLog(@"The wife's old is %d,The husband's old is %d " ,wifeOld, husbandOld); self.title = @"预测结果"; UILabel* label = [[[UILabel alloc] initWithFrame:CGRectMake( 10,80, 300, 50 )] autorelease]; NSString* longText =@"恭喜您们! 您们怀上的是"; label.text = [longText stringByAppendingFormat:@"%@ !",sex]; label.numberOfLines = 2; label.backgroundColor = [UIColor blackColor]; label.textColor = [UIColor whiteColor]; [self.view addSubview:label]; //追加动画图片,动画图片实际上由两张样子相近的图片组成 UIImage* chara1 = [UIImage imageNamed:@"chara1.png"]; UIImage* chara2 = [UIImage imageNamed:@"chara2.png"]; imageView_ = [[[UIImageView alloc] init] autorelease]; imageView_.frame = CGRectMake( 0, 0, 64, 64 ); //以数组的形式设置动画的图片 imageView_.animationImages = [NSArray arrayWithObjects:chara1, chara2, nil]; //每0.5秒切换显示下一张图片 imageView_.animationDuration = 0.5; imageView_.alpha=0.7; imageView_.center = self.view.center; imageView_.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; [self.view addSubview:imageView_];
//在画面下方追加“重新开始”按钮 UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:@"重新开始" forState:UIControlStateNormal]; [button sizeToFit]; CGPoint newPoint = self.view.center; newPoint.y += 80; button.center = newPoint; [button addTarget:self action:@selector(buttonDidPush) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; } //计算年龄方法。实为计算两个日期(date1,date2)间间隔的年数 - (int)getOldFirstDate:(NSDate*)date1 compareDate:(NSDate*)date2{ int old = 0; NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease]; [formatter setDateFormat:@"yyyy"]; NSString* yearStr1 = [formatter stringFromDate:date1]; int year1 = [yearStr1 intValue]; NSString* yearStr2 = [formatter stringFromDate:date2]; int year2 = [yearStr2 intValue]; [formatter setDateFormat:@"MM"]; NSString* monthStr1 = [formatter stringFromDate:date1]; int month1 = [monthStr1 intValue]; NSString* monthStr2 = [formatter stringFromDate:date2]; int month2 = [monthStr2 intValue]; [formatter setDateFormat:@"dd"]; NSString* dayStr1 = [formatter stringFromDate:date1]; int day1 = [dayStr1 intValue]; NSString* dayStr2 = [formatter stringFromDate:date2]; int day2 = [dayStr2 intValue]; //计算得出年龄为实岁,即未过32岁生日前算31岁 if ((month2>month1)||(month1==month2 && day2 >= day1)) { old = year2-year1; }else { old = year2-year1-1; } return old; } //点击“重新开始”按钮时的处理 - (void)buttonDidPush { WifeBirthdayController *viewController = [[WifeBirthdayController alloc] init]; //调用pushViewController: animated:重新显示准妈妈生日输入画面 [self.navigationController pushViewController:viewController animated:YES]; } @end

预测结果画面有两个稍微有点复杂的处理,即画面下方追加的动画效果,计算年龄的算法。

首先介绍一下动画效果的实现方式,本例中的动画效果实际上是交替显示两个外观相近但有些许差别的图片。实现的操作步骤如下。

步骤1 导入动画用原始图片,设置到UIImageView对象的animationImages中。

UIImage* chara1 = [UIImage imageNamed:@"chara1.png"];
UIImage* chara2 = [UIImage imageNamed:@"chara2.png"];
imageView_ = [[[UIImageView alloc] init] autorelease];
imageView_.frame = CGRectMake( 0, 0, 64, 64 );
//以数组的形式设置动画的图片
imageView_.animationImages = [NSArray arrayWithObjects:chara1,
chara2, nil];

步骤2 设置导入图片显示的切换周期及UIImageView对象的其他属性(如坐标和透明度等)。

//每0.5秒切换显示下一张图片
imageView_.animationDuration = 0.5;
imageView_.alpha=0.7;
imageView_.center = self.view.center;

步骤3 将UIImageView对象追加到母View中。

[self.view addSubview:imageView_];

步骤4 动画开始。

[imageView_ startAnimating];

运行上述代码开始动画显示。本例为画面显示时显示动画,所以将上述代码追加到viewWillAppear:方法中。

步骤5 动画结束。

[imageView_ stopAnimating];

运行上述代码后,动画停止。

其次介绍一下计算年龄的算法。本例中为计算两个日期间的间隔(实岁年龄),先将两个日期拆分为年、月、日。然后根据月、日的大小比较计算年与年之间的差值,并保证所得差值为实岁(即未过32岁生日的以31岁来计算)。

21.5 调试及在模拟器上测试

Xcode中除了提供iPhone模拟器外,还提供iPad模拟器,在运行前必须选择具体的模拟器,本例中,我们必须首先选择iPhone模拟器,如图21.12所示。从上至下依次选择Simulator、Debug、iPhone Simulator 4.x。笔者在开发前安装了iOS 4.3版本(2011年4月)的SDK。尽管对于本章的性别预测程序而言,只要是4.0版本以上的SDK都可以运行,但此处还是选择使用了新版本的iPhone SDK。

图21.12 选择iPhone模拟器

完成以上操作后,单击Build and Run按钮,可以在iPhone模拟器上看到这个性别预测应用程序的运行效果。应用程序启动后,首先会看到如图21.1所示的准妈妈生日输入画面,选择合适的日期,单击“下一步”按钮后,会进入后面准爸爸生日输入画面。如果选择了不合适的日期,则显示如图21.13所示的错误提示信息,随后的画面与设计画面相一致。

为了更方便地进行程序调试,正如本系列丛书的第18章调试中介绍的那样,首先可以在有重要数据输出/输入的地方适当加入NSLog语句,及时将数据输出到履历中。下面是一个实际的例子。

[formatter setDateFormat:@"yyyy"];
NSString* yearStr = [formatter stringFromDate:datePicker_.date];
NSLog( @"selected birthday is %@", yearStr );

图21.13 错误提示信息

如果出现与预想结果不相符的结果,首先需要确认这些履历(履历可以在Console窗口中进行确认)。如果仅仅依靠这些履历,仍然不能找到原因,就需要借助Xcode提供的调试(Debug)工具进行调试了,具体调试方法请参照上一本书的第18章中关于调试的介绍。