《iOS6 application development》學習之路:No.3: 自定義選擇器
先看下程序跑起來的樣子吧,沒有做任何,任何界麵上的優化,所以請忽視醜陋的頁麵。
總共就兩個,第一個頁麵是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
最後更新:2017-04-03 07:57:08