2011年7月11日 星期一

iOS development: Ch7-1 多重組件挑選器與標籤列

製作這些挑選器我們不只有從物件庫丟到內容檢視上就可以了,我們還要提供委派與資料來源!挑選器會將許多動作交給委派來做! 其中最重要的是繪製每一列的東西! 挑選器會從委派取得它的資料

資料來源用來告知挑選器它有多少組件一起運作及每個組件由多少列組成!

資料來源與委派通常是同一個物件,而且也當成挑選器的檢視控制器用

------------------------------------------------標籤列--------------------------------------------------------
下面那排就是標籤列
先建立檔案

Groups&Files -> Classes->蘋果鍵N
UIViewController subclass
建立很多檔案

建立Tab Bar Controller 在MainWindow

只有兩個不夠

可以增加Tab Bar Item進來
像這樣拉進來
這樣就有5個了

把Buttonbar設為TabBar

實作日期挑選器

//  DatePickerViewController.h
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import


@interface DatePickerViewController : UIViewController {
UIDatePicker *datePicker; //建立日期選擇器
}

@property (nonatomic, retain) IBOutlet UIDatePicker *datePicker;
-(IBAction) buttonPressed;

@end



//  DatePickerViewController.m
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "DatePickerViewController.h"

@implementation DatePickerViewController

@synthesize datePicker; 

-(IBAction) buttonPressed{
NSDate *selected=[datePicker date]; 
//取得datePicker上的值
NSString *message=[[NSString alloc] initWithFormat:@"The date and time you selected is:%@", selected];
//將其顯示在警示表
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Date and Time Selected"
  message:message
delegate:nil
cancelButtonTitle:@"!"
otherButtonTitles:nil];
[alert show];
[alert release];
[message release];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSDate *now =[[NSDate alloc] init];
[datePicker setDate:now animated:NO];
[now release];
//[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
self.datePicker=nil;
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)dealloc {
[datePicker release];
    [super dealloc];
}
@end
實作單一組件挑選器
//  SingleComponentPickerViewController.h
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import


@interface SingleComponentPickerViewController : UIViewController {
UIPickerView *singlePicker;
NSArray *pickerData;
}

@property (nonatomic, retain) IBOutlet UIPickerView *singlePicker;
@property (nonatomic, retain) IBOutlet NSArray *pickerData;
-(IBAction) buttonPressed;

@end

//  SingleComponentPickerViewController.m
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "SingleComponentPickerViewController.h"


@implementation SingleComponentPickerViewController

@synthesize singlePicker,pickerData;

-(IBAction) buttonPressed{

NSInteger row=[singlePicker selectedRowInComponent:0];
NSString *selected = [pickerData objectAtIndex:row];
NSString *title = [[NSString alloc] initWithFormat:@"you selected %@!", selected];
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:title
  message:@"Thanks"
delegate:nil
cancelButtonTitle:@"you're Welcome"
otherButtonTitles:nil];
[alert show];
[alert release];
[title release];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSArray *array=[[NSArray alloc] initWithObjects: @"玫瑰花",@"薰衣草",@"桂花" ,@"洋甘菊",@"茉莉花",@"菊花",@"野菊花",@"金銀花",nil];
    self.pickerData=array;
[array release];
[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
self.singlePicker = nil;
self.pickerData = nil;
}


- (void)dealloc {
[singlePicker release];
[pickerData release];
    [super dealloc];
}

#pragma mark -
#pragma mark Picker Data Source Methods
-(NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1; //挑選器可以有1個以上的轉輪,這會告訴挑選器它應該顯示多少個轉輪
}

-(NSInteger) pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [pickerData count];
}

#pragma mark Picker Deleate Methods
-(NSString *)pickerView:(UIPickerView *)pickerView 
titleForRow:(NSInteger) row
  forComponent: (NSInteger) component{
return [pickerData objectAtIndex:row];
}
@end
實作多重組件挑選器
//  DoubleComponentPickerViewController.h
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import

#define kFillingComponent 0
#define kBreadComponent 1

//讓控制器接受委派與資料來源的協定
@interface DoubleComponentPickerViewController : UIViewController {
UIPickerView *doublePicker//宣告 outlet
NSArray *fillingTypes; //建立資料陣列
NSArray *breadTypes;
}

@property(nonatomic, retain) IBOutlet UIPickerView *doublePicker;
@property(nonatomic, retain) NSArray *fillingTypes;
@property(nonatomic, retain) NSArray *breadTypes;
-(IBAction) buttonPressed;

@end
//  DoubleComponentPickerViewController.m
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "DoubleComponentPickerViewController.h"
@implementation DoubleComponentPickerViewController
@synthesize doublePicker,fillingTypes,breadTypes;

-(IBAction) buttonPressed{
//使用KFillingComponent,kBreadComponent來指定組件
NSInteger fillingRow=[doublePicker selectedRowInComponent:kFillingComponent]; 
NSInteger breadRow=[doublePicker selectedRowInComponent:kBreadComponent];
NSString *bread=[breadTypes objectAtIndex: breadRow];
NSString *filling=[fillingTypes objectAtIndex:fillingRow];
NSString *message=[[NSString alloc] initWithFormat:@"your %@ on %@ bread will be right up.", filling, bread];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Thanks for your order"
message: message
  delegate: nil 
  cancelButtonTitle: @"Great!" 
  otherButtonTitles:nil];

[alert show];
[alert release];
[message release];
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
//在這裡載入兩個資料陣列
NSArray *fillingArray = [[NSArray alloc] initWithObjects:@"Ham", @"Turkey",@"Peanut Butter",@"Tuna Salad",@"Nutella",@"Roast Beef",@"Vegemite", nil];
self.fillingTypes= fillingArray;
[fillingArray release];
NSArray *breadArray=[[NSArray alloc] initWithObjects:@"White", @"Whole Wheat", @"Rye", @"Sourdough", @"Seven Grain", nil];
self.breadTypes = breadArray;
[breadArray release];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
[super viewDidUnload];
self.doublePicker = nil;
self.breadTypes=nil;
self.fillingTypes=nil;
}

- (void)dealloc {
[doublePicker release];
[breadTypes release];
[fillingTypes release];
    [super dealloc];
}

#pragma mark -
#pragma mark Picker Data Source Methods

//在資料來源方法中指定挑選器有兩個
-(NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 2;//挑選器有兩個
}

//我們需要檢查挑選器選到哪個組件,並回傳對應陣列中正確的列
-(NSInteger) pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
if(component==kBreadComponent)
return[ self.breadTypes count];
return [self.fillingTypes count];
}

#pragma mark Picker Delegate Methods
//在委派方法中利用剛剛的列所對應的陣列來抓取正確值並回傳
-(NSString *)pickerView:(UIPickerView *) pickerView 
titleForRow:(NSInteger) row 
  forComponent:(NSInteger) component{
if(component==kBreadComponent)
return [self.breadTypes objectAtIndex:row];
return [ self.fillingTypes objectAtIndex:row];
}
@end
實作相依組件挑選器



//  DependentComponentPickerViewController.h
//  Pickers
//
//  Created by Dave Mark on 12/5/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import
#define kStateComponent   0
#define kZipComponent     1

@interface DependentComponentPickerViewController : UIViewController
{
    UIPickerView    *picker;
    NSDictionary    *stateZips;
    NSArray         *states;
    NSArray         *zips;
}
@property (retain, nonatomic) IBOutlet UIPickerView *picker;
@property (retain, nonatomic) NSDictionary *stateZips;
@property (retain, nonatomic) NSArray *states;
@property (retain, nonatomic) NSArray *zips;
- (IBAction) buttonPressed;
@end

//  DependentComponentPickerViewController.m
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "DependentComponentPickerViewController.h"

@implementation DependentComponentPickerViewController

@synthesize picker;
@synthesize stateZips;
@synthesize states;
@synthesize zips;


-(IBAction) buttonPressed{

NSInteger stateRow = [picker selectedRowInComponent:kStateComponent];
NSInteger zipRow= [picker selectedRowInComponent:kZipComponent];
NSString *state = [self.states objectAtIndex:stateRow];
NSString *zip = [self.zips objectAtIndex:zipRow];
NSString *title= [[NSString alloc] initWithFormat:@"You selected zip code %@.", zip];
NSString *message= [[NSString alloc] initWithFormat:@"%@ is in %@", zip, state];
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:title
  message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
[title release];
[message release];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];//抓取此程式的主套件做為參考
//使用主套件取得資料來源
NSString *plistPath = [bundle pathForResource:@"statedictionary"
  ofType:@"plist"];//此程式將會回傳一個字串帶有stateddictionary.plist的路徑
NSDictionary *dictionary= [[NSDictionary alloc] initWithContentsOfFile:plistPath]; //利用這個路徑建立一個NSDictionary物件,這樣就可以把清單載入到dictionary裡了
self.stateZips = dictionary;//將此NSDictionary 物件指派給stateZips 
[dictionary release];
//剛載入的資料statdictionary.plist 是以州名為鍵值,而其中的NSArray則以此州內所有的郵遞區號當成值
//接下來做左邊的資料,左邊資料是以州名當成資料,所以就是取出此資料的所有鍵值然後指派給state,指派前我們要把它先按字母排序
NSArray *components = [ self.stateZips allKeys];//取出所有鍵值 放入 components
NSArray *sorted = [components sortedArrayUsingSelector:@selector (compare:)];//按字母排序
self.states = sorted;//將結果指派給state
//將挑選器初值設為第一列 (index = 0的那一列
NSString *selectedState = [self.states objectAtIndex:0]; //取得states的第一列的州名
NSArray *array = [stateZips objectForKey:selectedState];//取得此州名對應的郵遞區號陣列
self.zips = array;//然後再指派給zips
}
- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.picker = nil;
self.stateZips = nil;
self.zips = nil;
}

- (void)dealloc {
[picker release];
[stateZips release];
[states release];
[zips release];
    [super dealloc];
}

#pragma mark -
#pragma mark picker Data Source Methods

//先設定有兩個挑選列
-(NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 2;
}

//當我們被問到列的數目的時候要用這個方法回答正確列的數量
-(NSInteger) pickerView: (UIPickerView *) pickerView numberOfRowsInComponent: (NSInteger) component {
if (component == kStateComponent)//問到有幾個州
return [ self.states count];//回答states的數量
return [self.zips count];//否則就回郵遞區號的數量
}

#pragma mark Picker Delegate Methods
//當我們被問到列且對應的欄位的title的時候要用這個方法回答正確列且對應的欄位的title
- (NSString *) pickerView: (UIPickerView *) pickerView
  titleForRow:(NSInteger) row 
forComponent: (NSInteger) component{

if (component == kStateComponent)//如果問到州名抬頭
return [ self.states objectAtIndex:row]; //對應欄位的州名

return [self.zips objectAtIndex:row];//否則回答對應欄位的郵遞區號
}
//此方法會於選擇器變化時被呼叫
-(void) pickerView:(UIPickerView *)pickerView 
  didSelectRow:(NSInteger)row 
  inComponent:(NSInteger)component {
if(component == kStateComponent){//如果是左邊的州名挑選器被改變時
NSString *selectedState = [self.states objectAtIndex: row]; //看是選到哪個州
NSArray *array=[ stateZips objectForKey:selectedState]; //取得該州的 郵遞區號
self.zips =array; //指定給zips
//然後要跟右邊的挑選器說回到第一列
[picker selectRow:0 inComponent: kZipComponent animated:YES];
//還要重新載入自己
[picker reloadComponent:kZipComponent];
//[picker reloadAllComponents];
}
}
@end

實作自訂挑選器


//  CustomPickerViewController.h
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import
#import //啟動iOS 的音訊控制箱

@interface CustomPickerViewController : UIViewController {

UIPickerView *picker; //挑選器的檢視
UILabel *winLabel; //告知玩家贏了的標籤
NSArray *column1;
NSArray *column2;
NSArray *column3;
NSArray *column4;
NSArray *column5;
UIButton *button;
SystemSoundID crunchSoundID;
SystemSoundID winSoundID;
}

@property (nonatomic ,retain) IBOutlet UIPickerView *picker;
@property (nonatomic ,retain) IBOutlet UILabel *winLabel;
@property (nonatomic ,retain) NSArray *column1,*column2,*column3,*column4,*column5; //用來存放圖像檢視
@property (nonatomic ,retain) IBOutlet UIButton *button;
@property (nonatomic) SystemSoundID crunchSoundID;
@property (nonatomic) SystemSoundID winSoundID;

-(IBAction) spin;
@end
//  CustomPickerViewController.m
//  Ch7-1 Picker
//
//  Created by jason on 2011/7/3.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "CustomPickerViewController.h"

@implementation CustomPickerViewController

@synthesize picker;
@synthesize winLabel;
@synthesize column1,column2,column3,column4,column5;
@synthesize button;
@synthesize crunchSoundID, winSoundID;


-(void) showButton{ //這個方法用來顯示按鈕,當使用者點按完按鈕後這個按鈕就會被隱藏
button.hidden=NO;
}

-(void) playWinSound{//呼叫勝利者之歌
AudioServicesPlaySystemSound(winSoundID);//播放歌曲
winLabel.text =@"WIN!";//label 改為 WIN!
// [performSelector:withObject:afterDelay] 它可以在未來的某個時間點呼叫方法
[self performSelector:@selector (showButton) withObject: nil afterDelay:1.5];//1.5秒後呼叫showButton 
}

-(IBAction) spin{
BOOL win=NO; // 此變數會追蹤挑選器上同一列是否出現三個同樣的符號
int numInRow = 1; //此變數用來追蹤目前同一列有多少相同符號出現
int lastVal = -1; //此變數用來記錄上一個符號值,用來比對目前的符號值
for (int i=0; i<5; i++){
int newValue = random()%[self.column1 count]; // 以亂數取得新的值, [self.column1 count] => 取得 column1 的陣列數量
if (newValue == lastVal)
numInRow++;
else 
numInRow = 1;
lastVal = newValue;
[picker selectRow: newValue inComponent:i animated:YES];//將要對應的組件設定為新值,設定動畫效果
[picker reloadComponent:i];//讓選擇器重載這個組件
if (numInRow >= 3) //判斷是否一列有三個一樣的狀況
win=YES;
}
button.hidden = YES;//spin隱藏
AudioServicesPlaySystemSound(crunchSoundID); //播放音效
if(win)
[self performSelector:@selector(playWinSound) withObject:nil afterDelay:0.5];//呼叫剛剛建立的方法 ,播放勝利者之歌
else
[self performSelector:@selector(showButton) withObject:nil afterDelay:0.5]; //否則就顯示按鈕
winLabel.text = @"";
}


// The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization.
    }
    return self;
}
*/


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
//載入六種不同的圖案
UIImage *seven = [UIImage imageNamed:@"seven.png"];
UIImage *bar = [UIImage imageNamed:@"bar.png"];
UIImage *cherry = [UIImage imageNamed:@"cherry.png"];
UIImage *crown = [UIImage imageNamed:@"crown.png"];
UIImage *apple = [UIImage imageNamed:@"apple.png"];
UIImage *lemon = [UIImage imageNamed:@"lemon.png"];
for (int i =1; i<= 5; i++){
//為六種圖案建立實例
UIImageView *sevenView = [[UIImageView alloc] initWithImage: seven];
UIImageView *barView = [[UIImageView alloc] initWithImage: bar];
UIImageView *cherryView = [[UIImageView alloc] initWithImage: cherry];
UIImageView *crownView = [[UIImageView alloc] initWithImage: crown];
UIImageView *appleView = [[UIImageView alloc] initWithImage: apple];
UIImageView *lemonView = [[UIImageView alloc] initWithImage: lemon];
//將圖案放入圖案陣列中
NSArray *imageViewArray = [[NSArray alloc] initWithObjects: sevenView, barView, crownView, appleView,lemonView,cherryView,nil];
//將圖案陣列指派給我們要顯示的列(column *)的陣列 
NSString *fieldName = [[ NSString alloc] initWithFormat:@"column%d", i];//建立與我們定義的陣列名稱相同的字串
[self setValue:imageViewArray forKey: fieldName];//將剛剛做出來的字串套入 setValue ; => 等同於 [ self setcolumn1: imageViewArray forKey: fieldName ]
//記憶體清除
[fieldName release];
[imageViewArray release];
[sevenView release];
[barView release];
[crownView release];
[cherryView release];
[lemonView release];
[appleView release];
}
NSString *path=[[NSBundle mainBundle] pathForResource:@"win" ofType:@"wav"];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path], &winSoundID);
path = [[NSBundle mainBundle] pathForResource:@"crunch" ofType:@"wav"];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path], &crunchSoundID);
//隨機產生新種子
srandom(time(NULL));
//    [super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
    [super viewDidUnload];
self.picker =nil;
self.winLabel = nil;
self.column1 = nil;
self.column2 = nil;
self.column3 = nil;
self.column4 = nil;
self.column5 = nil;
self.button = nil;
if(winSoundID)
AudioServicesDisposeSystemSoundID(winSoundID), winSoundID =0;
if(crunchSoundID)
AudioServicesDisposeSystemSoundID(crunchSoundID), crunchSoundID =0;
  }


- (void)dealloc {
[picker release];
[winLabel release];
[column1 release];
[column2 release];
[column3 release];
[column4 release];
[column5 release];
[button release];
if(winSoundID)
AudioServicesDisposeSystemSoundID(winSoundID), winSoundID =0;
if(crunchSoundID)
AudioServicesDisposeSystemSoundID(crunchSoundID), crunchSoundID =0;
    [super dealloc];
}


# pragma mark -
# pragma mark Picker Data Source Methods 
  -(NSInteger) numberOfComponentsInPickerView: (UIPickerView *) pickerView{
  return 5;
  }
  -(NSInteger) pickerView: (UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger) component {
  return [self.column1 count];
  }
 
# pragma mark Picker Delegate Methods
//之前都是使用NSString 為回傳值的委派方法 ,這次改為 UIView ,改成這個方法任何可以畫入UIView的方法都可以提供給挑選器
//這個方法會從五個陣列其中之一傳回一個影像檢視 (UIView) 
-(UIView *)pickerView:(UIPickerView *)pickerView //挑選器檢視 
  viewForRow:(NSInteger)row //哪一欄的挑選器檢視
forComponent:(NSInteger)component //索引值
  reusingView:(UIView *)view 
{
NSString *arrayName = [[NSString alloc] initWithFormat:@"column%d", component+1];//以陣列名稱建立字串 (因為 column1~5 是由1開始, component是由0開始,所以component+1 就會對應到正確的column
NSArray *array = [self valueForKey: arrayName];//利用 arrayName來擷取對應的陣列
[arrayName release];
return [array objectAtIndex:row];
}
@end