閱讀669 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《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];

    }

}


我標注出來了,在協議  datasource中有的方法,numberOfRowsInComponent,返回每個組件包含的元素數目。數組的數目可以用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

因為是iPhone的版本,需要手工關閉場景,如果是iPad,因為彈出來的是個對話框,所以點擊其他地方就可以關閉了,所以這個函數隻有在iPhone上才需要用到。













最後更新:2017-04-03 07:57:08

  上一篇:go chm 已取消到該網頁的導航,打不開!
  下一篇:go MP4文件格式的解析