先看下程序跑起来的样子吧,没有做任何,任何界面上的优化,所以请忽视丑陋的页面。
总共就两个,第一个页面是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