先看下程序跑起来的样子吧,没有做任何,任何界面上的优化,所以请忽视丑陋的页面。
总共就两个,第一个页面是inital页面,中间一个label用来显示用户做的不同选择,下方是一个放在tool bar里面的button,用户点击后可以进行两个页面的切换。第2个页面总共有3个空间,最上端是一个自定义的pick view,两列,第一列显示动物的图片,第二列显示动物的叫声。中间是一个label,最下端是一个按钮,点击按钮后用来返回上一个页面。
因为教材中是按照功能拆分了一块一块讲解的,所以这次分别把2个页面的最终接口和实现文件整体来看,也算是给自己做个回顾和总结。
前提:
需要在Xcode的项目导航器中新增一个类,名字叫AnimalChooserViewController。然后将这个类和我们新增加的页面关联起来,从类的名字也能看出来是第2个页面了。记得把两个页面在IB中的名字分别改成initial和Anima Chooser,这样做的好处就不多说了。
还要把一些资源文件拖进来,主要是7个动物的图片,这些都可以在这本书的网站上下载到。好了,下面就是4个重头戏了。
先来说initial页面的接口文件:ViewControll.h
#import <UIKit/UIKit.h> #import "AnimalChooserViewController.h" @interface ViewController : UIViewController @property (weak, nonatomic) IBOutlet UILabel *outputLabel; - (IBAction)showAnimalChosser:(id)sender; - (void) displayAnimal:(NSString* )chosenAnimal withSound:(NSString* )chosenSound fromComponent:(NSString* )chosenComponent; @property (nonatomic) Boolean animalChooserVivible; @end
很简单有没有,
这里面我们要import进来我们新建的那个类,因为要做交互啊。一个控件 outputLabel 和函数 showAnimalChosser 都是很简单的,通过IB里面鼠标拖动来添加,这些都再简单不过了。我们需要一个新的property: animalChooserVisibile来存储第2个场景的当前可见性。同时还有一个自定义的函数,displayAnimal,有3个参数:chosenAnima、chosenSound和chosenComponent,这个函数是用来接收第2个场景传递过来的信息并显示在label上的。
下面上ViewControll.m文件:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)showAnimalChosser:(id)sender { if(self.animalChooserVivible !=YES){ [self performSegueWithIdentifier:@"toAnimalChooser" sender:sender]; //preform the animal chooser sence self.animalChooserVivible = YES; } } - (void) displayAnimal:(NSString *)chosenAnimal withSound:(NSString *)chosenSound fromComponent:(NSString *)chosenComponent{ NSString* animalSoundString; animalSoundString = [[NSString alloc] initWithFormat:@"You changed %@ (%@ and the sound %@)", chosenComponent,chosenAnimal,chosenSound]; self.outputLabel.text = animalSoundString; } - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ ((AnimalChooserViewController *) segue.destinationViewController).delegate = self; } @end
实现文件主要完成3个函数:showAnimalChooser是用户点击toolbar上的按钮时进行的函数:判断当第2个场景可显示时,进行切换。注意,一定要在IB中把场景切换命名为toAnimaChooser,否则是无法完成手动切换的。
displayAnimal函数根绝接受到的3个参数,修改label的文字内容内容,这个函数会在第2个界面上点击Done之后被调用。
prepareForSeque函数:为了使用属性delegate来访问initial场景,我们马上将看到在AnimalChooserViewController的接口文件中会加入属性,所以在这里调用此函数来设置initial场景的属性。
AnimalChooserViewController.h:
#import <UIKit/UIKit.h> #import "ViewController.h" @class ViewController; @interface AnimalChooserViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate> @property (weak, nonatomic) id delegate; - (IBAction)dismissAnimalChosser:(id)sender; - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender; @end @interface AnimalChooserViewController(){ NSArray* _animalNames; NSArray* _animalSounds; NSArray* _animalImages; } @end看到了新添的属性 id delegate,用来访问initial场景。
一个函数: dismissAnimalChooser,当用户点击DONE按钮时,退回到initial界面。
这里需要将3个数组声名为私有变量,OC的私有变量是这样声名的,记一下了。
为什么要声名3个数组是为了更好的现实动物的名字,因为原始的图片是带了.png的,我们当然不希望把这个现实出来,所以要重新映射一下名字,以及声音和图片。
注意到在interface AnimalChooserViewController: UIViewController 后面还加上了 <UIPickerViewDataSource, UIPickerViewDelegate>,作用是为了调用自定义选择器的几个函数。在实现文件中会用到的。
AnimalChooserViewController.m:
#import "AnimalChooserViewController.h" #define kComponentCount 2 //define how many columns will be performed in the pick view #define kAnimalComponent 0 //the index for the first column #define kSoundComponent 1 //index for the second column @interface AnimalChooserViewController () @end @implementation AnimalChooserViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. _animalNames = @[@"Mouse", @"Goose", @"Cat",@"Dog",@"Snake",@"Bear",@"Pig"]; _animalSounds = @[@"Oink",@"Rawr",@"Ssss",@"Roof",@"Meow",@"Honk",@"Squeak"]; _animalImages = @[ [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mouse.png"]], [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"goose.png"]], [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cat.png"]], [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dog.png"]], [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"snake.png"]], [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bear.png"]], [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pig.png"]], ]; } - (void) viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; //Do any additional after view has been displayed ViewController* initialView; initialView = (ViewController*)self.delegate; [initialView displayAnimal:_animalNames[0] withSound:_animalSounds[0] fromComponent:@"nothing yet be chooosen"]; } - (void)viewWillDisappear:(BOOL)animated{ ((ViewController *)self.delegate).animalChooserVivible = NO; } - (NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView { return kComponentCount; } //founctions in the protocol "UIPickerViewDataSource" - (NSInteger) pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ if(component == kAnimalComponent){ return [_animalNames count]; }else { return [_animalSounds count]; } } //-----------------------------------------------------------------------// // founctions in the protocol "UIPickerViewDelegate" - (UIView* ) pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view{ if (component == kAnimalComponent){ return _animalImages[row]; } else { UILabel* soundLabel; soundLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 32)]; soundLabel.backgroundColor = [UIColor clearColor];// make the Rect into the background, because the color is transparent soundLabel.text = _animalSounds[row]; return soundLabel; } } //set the height of one single row - (CGFloat) pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{ return 55.0; } //set the width of the diffenret column - (CGFloat) pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component{ if(component == kAnimalComponent){ return 75.0; }else { return 150.0; } } //after user selected one row - (void) pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ ViewController* initialView; initialView = (ViewController* )self.delegate; if(component == kAnimalComponent){ int chosenSound = [pickerView selectedRowInComponent:kSoundComponent]; [initialView displayAnimal:_animalNames[row] withSound:_animalSounds[chosenSound] fromComponent:@"the Animal"]; } else{ int chosenAnimal = [pickerView selectedRowInComponent:kAnimalComponent]; [initialView displayAnimal:_animalNames[chosenAnimal] withSound:_animalSounds[row] fromComponent:@"the Sound"]; } } //-----------------------------------------------------------------------// - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ - (IBAction)dismissAnimalChosser:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; // manual close this sence. } @end
实现文件有点长,不要急,分开来看行了。
首先#define了几个名称,我都用注释标出来了。
在ViewDidLoad函数中,初始化了3个私有数组,
viewDidAppear函数中,初始化界面,把选择器都设置成第0帐图片和第0个声音,然后文字设置成 nothing yet been chosen。其实就是默认的选择器状态。注意这里其实就已经调用了initial界面的函数, 如果用户没有做任何改变返回第一个界面,可以给用户正确的提示。
viewWillDisappear
将initial用到的属性设置成NO,numberOfComponentsInPickerView
是我们在头文件中看到了<>里面包含的协议里面的函数,这个方法返回选择器将显示几个组件,我们这里就两列,数字已经#define过了//founctions in the protocol "UIPickerViewDataSource"
- (NSInteger) pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
if(component == kAnimalComponent){
return [_animalNames count];
}else {
return [_animalSounds count];
}
}
// founctions in the protocol "UIPickerViewDelegate" - (UIView* ) pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view{ if (component == kAnimalComponent){ return _animalImages[row]; } else { UILabel* soundLabel; soundLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 32)]; soundLabel.backgroundColor = [UIColor clearColor];// make the Rect into the background, because the color is transparent soundLabel.text = _animalSounds[row]; return soundLabel; } } //set the height of one single row - (CGFloat) pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{ return 55.0; } //set the width of the diffenret column - (CGFloat) pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component{ if(component == kAnimalComponent){ return 75.0; }else { return 150.0; } } //after user selected one row - (void) pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ ViewController* initialView; initialView = (ViewController* )self.delegate; if(component == kAnimalComponent){ int chosenSound = [pickerView selectedRowInComponent:kSoundComponent]; [initialView displayAnimal:_animalNames[row] withSound:_animalSounds[chosenSound] fromComponent:@"the Animal"]; } else{ int chosenAnimal = [pickerView selectedRowInComponent:kAnimalComponent]; [initialView displayAnimal:_animalNames[chosenAnimal] withSound:_animalSounds[row] fromComponent:@"the Sound"]; } } //-----------------------------------------------------------------------//
这几个函数都是DateViewDelegate协议中有的方法。当你在头文件中包含了这个协议,在接口文件中编译器会自动补全你想要写的函数的,good UE!
第1个函数给每个选择器元素提供自定义视图,这里手动动态添加了label,同时必须有2个的return值,否则XCode会提示你错误。这里注意到label的background用的时 clearColor,返回一个透明的颜色对象,否则矩形就不会融合到选择器视图的背景中。
第2个函数设置了每行的高度
第3个函数设置了不同列的宽度,这些数字都可以通过不断的纠错来达到你最满意的效果,话说回来可能是程序比较小,总感觉iOS的系统部署起来非常快,比android快多了。。。。
最后一个函数是在用户做出选择时的响应,其实就是给要传递给initial页面的3个参数附上正确的值。
实现文件的最后一个函数
- (IBAction)dismissAnimalChosser:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil]; // manual close this sence.
}
@end