从零开始学ios开发(十一):Tab Bars和Pickers

不好意思各位,本人休息了一个礼拜,所以这次的进度延后了,而且这次的学习的内容比较多,时间用的也比较长,文章发布的时间间隔有些长了,望各位谅解,下面继续我们的ios之旅。

这次我们主要学习的内容有2个,一个是Tab Bar,如下图 从零开始学ios开发(十一):Tab Bars和Pickers 很熟悉的界面(iphone中的phone),另一个Picker,如下图

从零开始学ios开发(十一):Tab Bars和Pickers

在正是开始学习项目之前,先首先简单介绍一下这次的例子的一个结构,当然一个root controller肯定是有的,用来控制其他subController的切换,在root controller中会放置一个tab bar,这个tab bar中有5个item,分别对应5个不同的view,每个view中有一个picker,所以在这个例子中,会有5个功能不同的picker,基本上涵盖了日常可能会用到的常规情况。OK,下面开始这次的学习。

1)创建一个工程,选择Empty Application,并命名为Pickers 这个过程和前一篇是一样的,在这里就不再叙述了。

2)添加需要的文件 之前我们说过,这个项目会有5个subcontrollers,因此我们需要添加5个view,选中Project navigator中的Pickers文件夹,单击鼠标右键,选择“New File...”,或者使用快捷键command+N,或者使用菜单栏File>New>New File... 从零开始学ios开发(十一):Tab Bars和Pickers 从零开始学ios开发(十一):Tab Bars和Pickers

在弹出的窗口中,左边选择Cocoa Touch,右边选择UIViewController subclass,点击Next按钮。

在下一个窗口中,填写类名BIDDatePickerViewController,并确认“With XIB for user interface” checkbox被选中(在前一篇的学习中,我们没有选中这个checkbox,而是在之后手动的连接controller和xib文件,在这里系统将帮我们连接这2个文件),点击Next 从零开始学ios开发(十一):Tab Bars和Pickers

选择保持的位置,保存在“Pickers”目录下,完成创建。创建完成后的Project navigator如下 从零开始学ios开发(十一):Tab Bars和Pickers 一共有3个文件被创建,分别是.h、.m和.xib。

使用相同的方法再创建其他4个subcontroller,分别命名为BIDSingleComponentPickerViewController、BIDDoubleComponentPickerViewController、BIDDependentComponentPickerViewController、BIDCustomPickerViewController。创建完成后的Project navigator如下: 从零开始学ios开发(十一):Tab Bars和Pickers 其中: BIDDatePickerViewController:包含一个Date Picker BIDSingleComponentPickerViewController:包含一个Picker View,并有一个滚轴1一个component) BIDDoubleComponentPickerViewController:包含一个Picker View,并有两个滚轴(2个component),且这2个滚轴是独立的 BIDDependentComponentPickerViewController:包含一个Picker View,并有两个滚轴(2个component),且这2个滚轴是联动的 BIDCustomPickerViewController:包含一个Picker View,并有五个滚轴(5个component),这5个滚轴中的内容是图片

(温馨提示:每一步做完后,你可以先build一下你的code,看看有没有错误,有的话可以及时修改,有一些警告应该是正常的,我们并没有完全完成项目的创建,但是不应该报错。)

3)添加Root View Controller(就是前面的提到的root controller) 添加root controller选择的模板和前面的有些不同,选中Project navigator中的Pickers文件夹,使用快捷键command+N创建一个新文件,在弹出的选中模板对话框中,左边选择User Interface,右边选择Empty模板,单击Next 从零开始学ios开发(十一):Tab Bars和Pickers Device Family保持默认的iPhone,点击Next,将新建的模板命名为TabBarController.xib,点击Create,创建完成。然后一个单独的TabBarController.xib文件会出现在Project navigator中。(Empty模板不会帮我们创建任何.h.m文件,我们会将TabBarController.xib关联到BIDAppDelegate,因此我们不需要和前面的文件一样创建.h.m文件,其次Empty模板不会帮我们创建一个默认的View,点击TabBarController.xib,你会发现xib的窗口中什么东西也没有,我们需要在之后自己添加)

打开BIDAppDelegate.h文件,添加以下内容

从零开始学ios开发(十一):Tab Bars和Pickers
#import <UIKit/UIKit.h>

@interface BIDAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) IBOutlet UITabBarController *rootController;

@end
从零开始学ios开发(十一):Tab Bars和Pickers

 打开BIDAppDelegate.m文件,添加以下内容

从零开始学ios开发(十一):Tab Bars和Pickers
#import "BIDAppDelegate.h"

@implementation BIDAppDelegate

@synthesize window = _window;
@synthesize rootController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    
    [[NSBundle mainBundle] loadNibNamed:@"TabBarController" owner:self options:nil];
    [self.window addSubview:rootController.view];
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

...
从零开始学ios开发(十一):Tab Bars和Pickers

这些方法应该比较熟悉了,和之前几篇的例子很相似。

在此打开TabBarControll.xib,在Object library中找到Tab Bar Controller 从零开始学ios开发(十一):Tab Bars和Pickers 并拖动到xib窗口 从零开始学ios开发(十一):Tab Bars和Pickers

下面的一步是将TabBarControll.xib关联到BIDAppDelegate,选中TabBarControll.xib,然后选择File‘s Owner,打开identity inspector,Class中选择BIDAppDelegate 从零开始学ios开发(十一):Tab Bars和Pickers 接着打开connections inspector,在Outlets中会出现我们刚在定义在BIDAppDelegate.h中的rootController,鼠标放到rootController右边的圆圈上,圆圈中会出现加号,然后点击鼠标拖动到xib窗口中的Tab Bar Controller上,将2者关联起来,之后在rootController边上就会显示Tab Bar Controller了。 从零开始学ios开发(十一):Tab Bars和Pickers

4)添加Tab Bar Item 首先可以看一下Tab Bar Controller的结构 从零开始学ios开发(十一):Tab Bars和Pickers 一个Tab Bar Controller下面有一个Tab Bar,然后这个Tab Bar下有很多Item(每个Tab Bar Controller默认会有2个tab bar item),每个Item有一个View Controller,每个View Controller下又有一个Tab Bar Item,好了这个就是一个Tab Bar Controller的基本结构,因为我们需要5个tab bar item,下面就添加其他的3个。

在Object Library中找到Tab Bar Item 从零开始学ios开发(十一):Tab Bars和Pickers 然后拖动到Tab Bar上,一共拖3个,位置无所谓,添加完tab bar item的结构如下 从零开始学ios开发(十一):Tab Bars和Pickers 从零开始学ios开发(十一):Tab Bars和Pickers 这2者是一一对应的。

5个tab bar item分别对应5个view,因此我们需要制定2者的对应关系,选中最左边的一个tab bar item,打开attribute inspector,在NIB Name的下拉列表中选择BIDDatePickerViewController,然后打开identity inspcetor,在Class中选择BIDDatePickerViewController(在NIB Name中选择BIDDatePickerViewController是指定tab bar item对应的view是哪个,在Class中选中BIDDatePickerViewController是指定view的delegate是哪个类),对其他的4个tab bar item都执行上面的操作,分别对应各自的controller。从左到右的顺序为: BIDDatePickerViewController BIDSingleComponentPickerViewController BIDDoubleComponentPickerViewController BIDDependentComponentPickerViewController BIDCustomPickerViewController

5)为Tab Bar Item添加icon 下载TabBarIcons,并其解压后连同文件夹一同拖入到Project navigator中,在弹出的“Choose options for adding these files”框中保持默认选项即可,添加完后的Project navigator如下 从零开始学ios开发(十一):Tab Bars和Pickers 一般来说,app中图片基本上都是使用png格式,然后Tab Bar Item的图片大小为24px * 24px。(感觉xcode还是很只能的,它能够自己搜索所有的在项目中的图片文件,然后供用户进行选择,无论你图片放在那个文件夹下,xcode都可以找到)

图片拖动到项目中后,接下来就是为tab bar item添加图片了,首先选中最左边的一个Tab Bar Item,选中的方法是在视图中点击2次最左边的item图标,第一次我们选中的是view controller,第二次才是真正的tab bar item 从零开始学ios开发(十一):Tab Bars和Pickers 从零开始学ios开发(十一):Tab Bars和Pickers 第二次选中后,打开Attributes inspector,在Bar Item的Image中找到clockicon.png,这样icon就设好了,然后将Title命名为Date即可。从零开始学ios开发(十一):Tab Bars和Pickers

其他的四个item都是相同的操作,对应关系如下 BIDDatePickerViewController(clockicon.png,Title:Date) BIDSingleComponentPickerViewController(singleicon.png,Title:Single) BIDDoubleComponentPickerViewController(doubleicon.png,Title:Double) BIDDependentComponentPickerViewController(dependenticon.png,Title:Dependent) BIDCustomPickerViewController(toolicon.png,Title:Custom) 完成后的样子 从零开始学ios开发(十一):Tab Bars和Pickers

至此,我们对于root controller的所有设置都完成了,编译运行一下程序,看看效果,之后在进行每个subcontroller的实现。

6)Date Picker 第一个实现的是Date Picker,首先打开BIDDatePickerViewController.h,添加如下代码

从零开始学ios开发(十一):Tab Bars和Pickers
#import <UIKit/UIKit.h>

@interface BIDDatePickerViewController : UIViewController

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

@end
从零开始学ios开发(十一):Tab Bars和Pickers

声明一个UIDatePicker的Outlet,用于等会和DatePicker控件连接,然后声明一个Action事件,我们会添加一个button,会触发该事件。

接着打开BIDDatePickerViewController.xib,然后在Graphical Layout Area(简称GLA)中选中View,再打开Attributes inspector,在Simulated Metrics中找到Bottom Bar,并选择“Tab Bar”,如下 从零开始学ios开发(十一):Tab Bars和Pickers 设置完成后,在GLA中会出现一条黑色区域,表示Tab Bar area,这样这个View的实际大小会把Tab Bar area给去掉,实际的高度变成411px 从零开始学ios开发(十一):Tab Bars和Pickers

在Object library中找到Date Picker 从零开始学ios开发(十一):Tab Bars和Pickers 并拖动到View的顶部,接着拖一个button放在DatePicker的下方,并命名为Select,完成后的效果如下 从零开始学ios开发(十一):Tab Bars和Pickers

我们还需要一个设置,设置DatePicker的最大日期和最小日期,选中View上的DatePicker,然后打开Attributs inspector,在Date Picker栏找到Constraints,将2个checkbox勾上 从零开始学ios开发(十一):Tab Bars和Pickers (其他的设置选项你自己也可以随便玩玩,看看DatePicker有什么变化)

7)连接Outlet和Action,写code 选中View上的button,打开connections inspector,在Sent Events中找到Touch Up Inside,将其连接到File‘s Owner下的buttonPressed action 选中File‘s Owner,control-drag到View中的DatePicker上,选择Outlet:datePicker 从零开始学ios开发(十一):Tab Bars和Pickers

好了,所有的连接都完成了,下面就可以写code了

打开BIDDatePickerViewController.m,添加如下code

从零开始学ios开发(十一):Tab Bars和Pickers
@implementation BIDDatePickerViewController
@synthesize datePicker;

......

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSDate *now = [NSDate date];
    [datePicker setDate:now animated:NO];
}

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

......

- (IBAction)buttonPressed
{
    NSDate *selected = [datePicker date];
    NSString *message = [[NSString alloc] initWithFormat:@"The date and time you selectd is: %@", selected];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Date and Time Selected"
                                                    message:message
                                                   delegate:self
                                          cancelButtonTitle:@"Yes, I do"
                                          otherButtonTitles:nil];
    [alert show];
}
从零开始学ios开发(十一):Tab Bars和Pickers

这些code应该比较容易理解,在viewDidLoad中初始化datePicker显示的日期,在viewDidUnload中释放datePicker,在buttonPressed方法中,首先获取datePicker当前选中的时间对象NSDate,然后赋给一个string,最后alert显示出来,非常简单。

编译运行code 从零开始学ios开发(十一):Tab Bars和Pickers

点击Select按钮,一个alert框弹出

从零开始学ios开发(十一):Tab Bars和Pickers

一切正常唯有时间不对,iphone上方显示的是11:13 AM,但是alert中显示的时间是03:11:13 +0000,这是它显示的是格林尼治时间,和中国的时差有8个小时,要解决这个问题,改写buttonPressed如下

从零开始学ios开发(十一):Tab Bars和Pickers
- (IBAction)buttonPressed
{
    NSDate *selected = [datePicker date];
    
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    NSInteger interval = [zone secondsFromGMTForDate: selected];
    NSDate *localeDate = [selected  dateByAddingTimeInterval: interval]; 
    NSString *message = [[NSString alloc] initWithFormat:@"The date and time you selectd is: %@", loaclDate];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Date and Time Selected"
                                                    message:message
                                                   delegate:self
                                          cancelButtonTitle:@"Yes, I do"
                                          otherButtonTitles:nil];
    [alert show];
}
从零开始学ios开发(十一):Tab Bars和Pickers

NSTimeZone *zone = [NSTimeZonesystemTimeZone]; // 获得当前设备的时区,即iphone的时区

NSInteger interval = [zone secondsFromGMTForDate: selected]; // 算出当前时区和GMT的时差是多少

NSDate *localeDate = [selected  dateByAddingTimeInterval: interval]; // 在GMT的基础上加上加上相应的时差

好了,在此编译运行,点击button后,显示的时间和iphone上的一致了 从零开始学ios开发(十一):Tab Bars和Pickers

8)Single-Component Picker DatePicker是一个特殊的picker,专门用于对日期进行选择,但是在很多时候,我们需要选择的内容都是需要我们自己制定的,在这个例子中,我们就将制定自己内容。 在这里例子中,我们会添加一个Picker View 从零开始学ios开发(十一):Tab Bars和Pickers 在Picker中,每一个滚轴就是一个component,我们将使用delegate来定义Picker View中component的数量和数据的来源。

9)创建Outlet和Action 打开BIDSingleComponentPickerViewController.h,添加如下code

从零开始学ios开发(十一):Tab Bars和Pickers
#import <UIKit/UIKit.h>

@interface BIDSingleComponentPickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>

@property (strong, nonatomic) IBOutlet UIPickerView *singlePicker;
@property (strong, nonatomic) NSArray *pickerData;

- (IBAction)buttonPressed;

@end
从零开始学ios开发(十一):Tab Bars和Pickers

首先添加了2个protocols,这2个protocols是必须的,PickerView的delegate和datasource都是源于它,然后声明了一个指向PickerView的对象,再声明一个保存PickerView数据的数列,最后声明了一个action,代码很简单。

10)添加Picker View,绑定Outlet和Action 选中BIDSingleComponentPickerViewController.xib,空出底部的Tab bar位置(Attributes inspector>Simulated Metrics>Bottom Bar>Tab Bar。在Object library中,找到Picker View,拖动到View的顶部,在拖一个button放在Picker View的下面,命名为Select,所有控件添加完毕 从零开始学ios开发(十一):Tab Bars和Pickers

选中File‘s Owner,control-drag到Picker View上,选中singlePicker。 选中Picker View,打开connections inspector,拖动dataSource和delegate边上的圆圈到File‘s Owner上进行绑定,这样就告诉这个Picker View,它的dataSource和delegate都是BIDSingleComponentPickerViewController类 从零开始学ios开发(十一):Tab Bars和Pickers 绑定Select按钮的Touch Up Inside的action为buttonPressed(方法和之前的一个例子一样)

ok,至此所有的控件和连接操作都已经完成,下面开始code部分。

11)写code 打开BIDSingleComponentPickerViewController.m,添加如下代码

从零开始学ios开发(十一):Tab Bars和Pickers
#import "BIDSingleComponentPickerViewController.h"

@implementation BIDSingleComponentPickerViewController
@synthesize singlePicker;
@synthesize pickerData;

......

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSArray *array = [[NSArray alloc] initWithObjects:@"Luke", @"Leia",
                      @"Han", @"Chewbacca", @"Artoo", @"Threepio", @"Lando", nil];
    self.pickerData = array;
}

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

......

- (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:@"Thank you for choosing."
                                                   delegate:nil
                                          cancelButtonTitle:@"You‘re Welcome"
                                          otherButtonTitles:nil];
    [alert show];
}
从零开始学ios开发(十一):Tab Bars和Pickers

一些必要的解释,在viewDidLoad中,声明一个数组,存放在PickerView的componnent中显示的数据。 buttonPressed方法中: NSInteger row = [singlePicker selectedRowInComponent:0]; // selectedRowInComponent方法返回当前component滚到哪一行,后面的0表示最左边的一个component NSString *selected = [pickerData objectAtIndex:row]; // objectAtIndex:row返回component中当前行的对象,这个例子中是NSString

其他的代码都比较简单,下面继续添加code,这次添加dataSource和delegate需要使用到的方法,这也是每个Picker View(不包括Date Picker)都需要实现的方法

从零开始学ios开发(十一):Tab Bars和Pickers
#pragma mark -
#pragma mark Picker Data Source Methods

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 1;
}

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

#pragma mark Picker Delegate Methods
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    return [pickerData objectAtIndex:row];
}
从零开始学ios开发(十一):Tab Bars和Pickers

numberOfComponentsInPickerView告诉picker一共有几个component(滚轴),我们这个例子是SingleComponent,只有一个滚轴,因此返回1。这个方法有一个参数是传一个pickerview进来,如果一个view中有2个或2个以上的Picker View,就需要使用这个参数,告诉系统到底是在设置哪个Picker View的component的数量,如果只有1个Picker View就可以忽略该参数,我们这个例子就忽略了这个参数。

pickerView方法是返回dataSource的数量,告诉pick你有多少个数据需要显示。参数pickerView指明是哪个picker在调用,只有一个的话可以忽略该参数,参数component是picker中的第几个滚轴,如果只有一个滚轴,也可以忽略该参数。

以上是的2个方法都是属于dataSource,最后的一个方法是属于delegate的,Picker View的dataSource方法是必须实现的,而delegate方法是可选的,但必须要实现其中的一个,在这个例子中我们实现的是pickerView方法 pickerView方法在指定的picker和component中返回第row行数据,这个例子中的数据都来自NSArray,就返回array中的第row个数据即可。在这个方法中,只用到了参数row,其他2个参数不需要,因为我们只有1个pickerview和1个component。

至此,所有的Single component的所有code都写完了,编译运行,选择Tab Bar上的第二个item 从零开始学ios开发(十一):Tab Bars和Pickers 单击Select按钮,警告框弹出,显示哪一行的内容被选中了 从零开始学ios开发(十一):Tab Bars和Pickers

13)Multicomponent Picker 在这个例子中,将会有2个component,它们各自独立(没有关联),实现起来也很简单,只要准备2个NSArray,在返回component的数量的方法中返回2就可以了,下面进行具体的实现。

14)创建Outlet和Action 打开BIDDoubleComponentPickerViewController.h,添加如下code

从零开始学ios开发(十一):Tab Bars和Pickers
#import <UIKit/UIKit.h>

#define kfillingComponent 0
#define kBreadComponent 1

@interface BIDDoubleComponentPickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>

@property (strong, nonatomic) IBOutlet UIPickerView *doublePicker;
@property (strong, nonatomic) NSArray *fillingTypes;
@property (strong, nonatomic) NSArray *breadTypes;

- (IBAction)buttonPressed;

@end
从零开始学ios开发(十一):Tab Bars和Pickers

上面的code应该很容易理解,定义了2个宏变量来区分左右2个component,这是一个很好的习惯,而且使代码更易读,然后添加了2个protocols,声明了一个指向PickerView的outlet,声明2个数组,分别保存左右2个component的数据,最后定义一个action,很简单。

15)添加Picker View,绑定Outlet和Action 打开BIDDoubleComponentPickerViewController.xib,设置view的Bottom Bar为Tab Bar,拖一个Picker View放到view的顶部,拖一个按钮放到Picker View的下面,并命名为Select,完成后的界面 从零开始学ios开发(十一):Tab Bars和Pickers

选中File‘s Owner,control-drag到Picker View上,选中doublePicker。 选中Picker View,打开connections inspector,拖动dataSource和delegate边上的圆圈到File‘s Owner上进行绑定,这样就告诉这个Picker View,它的dataSource和delegate都是BIDDoubleComponentPickerViewController类 绑定Select按钮的Touch Up Inside的action为buttonPressed

16)写code 打开BIDDoubleComponentPickerViewController.m文件,添加如下代码

从零开始学ios开发(十一):Tab Bars和Pickers
@implementation BIDDoubleComponentPickerViewController
@synthesize doublePicker;
@synthesize fillingTypes;
@synthesize breadTypes;

- (IBAction)buttonPressed
{
    NSInteger fillingRow = [doublePicker selectedRowInComponent:kfillingComponent];
    NSInteger breadRow = [doublePicker selectedRowInComponent:kBreadComponent];
    
    NSString *filling = [fillingTypes objectAtIndex:fillingRow];
    NSString *bread = [breadTypes objectAtIndex:breadRow];
    
    NSString *message = [[NSString alloc] initWithFormat:@"Your %@ on %@ bread will be right up.", filling,bread];
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Thank you for your order"
                                                    message:message
                                                   delegate:nil
                                          cancelButtonTitle:@"Great!" 
                                          otherButtonTitles:nil];
    [alert show];
}

......

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSArray *fillingArray = [[NSArray alloc] initWithObjects:@"Ham",
                             @"Turkey", @"Peanut Butter", @"Tuna Salad",
                             @"Nutella", @"Roast Beef", @"Vegemite", nil];
    self.fillingTypes = fillingArray;
    
    NSArray *breadArray = [[NSArray alloc] initWithObjects:@"White",
                           @"Whole Wheat", @"Rye", @"Sourdough", @"Seven Grain",nil];
    self.breadTypes = breadArray;

}

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

......

#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];
}
从零开始学ios开发(十一):Tab Bars和Pickers

上面的code和在singlepicker中的基本一样,由于有2个component,因此在viewDidLoad中创建了2个数组,在numberOfComponentsInPickerView中返回2,这样PickerView就会创建2个component(滚轴),在DataSource的pickerView方法中,根据component来返回数组,在delegate的pickerView中根据component返回数据。很简单,然后编译运行程序,载入app后,选择Tab Bar上的第三个tab item,出现如下界面 从零开始学ios开发(十一):Tab Bars和Pickers

随便滚动2个component,他们是独立的,不会相互影响 从零开始学ios开发(十一):Tab Bars和Pickers

点击Select按钮,一个警告框弹出,告知你选择了哪些数据 从零开始学ios开发(十一):Tab Bars和Pickers

17)Dependnet Components 前面的2个例子分别实现了1个componet和2个component,在这个例子中,也将实现2个component,但是这2个componet是联动的,当左边的component存放美国的各个洲(state),右边的component存放每个洲的邮政编码(zip),当左边的洲发生时,右边的邮政编码也会随之变化。另外在之前的例子中,显示在component中的数据都是写在code中的,这个不利于数据的更新,且如果数据量巨大,我们也不可能都写在code里面,在这个例子中,数据将存入一个property list(plist)文件,component的数据将从该文件中获取。

打开BIDDependentComponentPickerViewController.h文件,添加如下code

从零开始学ios开发(十一):Tab Bars和Pickers
#import <UIKit/UIKit.h> #define kStateComponent 0 #define kZipComponent 1

@interface BIDDependentComponentPickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>

@property (strong, nonatomic) IBOutlet UIPickerView *picker;
@property (strong, nonatomic) NSDictionary *stateZips;
@property (strong, nonatomic) NSArray *states;
@property (strong, nonatomic) NSArray *zips;

- (IBAction)buttonPressed;

@end
从零开始学ios开发(十一):Tab Bars和Pickers

唯一不同的是,多了一个NSDictionary对象,这个对象是用来存放关联数据的,存放之前提到的plist。

和前面2个例子一样,拖动一个Picker View和button到View上,并连接Outlet和Action 从零开始学ios开发(十一):Tab Bars和Pickers

18)NSDictionary:statedictionary.plist 下载statedictionary.plist压缩包,并解压出里面的文件statedictionary.plist,将statedictionary.plist拖入到Project navigator中,放到Pickers目录下,点击statedictionary.plist文件,你会发现有茫茫多的数据 从零开始学ios开发(十一):Tab Bars和Pickers

其实statedictionary.plist是一个xml文件,理解有序的将这些数据组织起来,供程序使用 从零开始学ios开发(十一):Tab Bars和Pickers

19)写code 由于这次的code稍微有些不同,主要是在读取plist上面,因此我们分开写,打开BIDDependentComponentPickerViewController.m文件,首先添加如下code

从零开始学ios开发(十一):Tab Bars和Pickers
#import "BIDDependentComponentPickerViewController.h"

@implementation BIDDependentComponentPickerViewController
@synthesize picker;
@synthesize stateZips;
@synthesize states;
@synthesize zips;

- (IBAction)buttonPressed
{
    NSInteger stateRow = [picker selectedRowInComponent:kStateComponent];
    NSInteger zipRow = [picker selectedRowInComponent:kZipComponent];
    
    NSString *state = [states objectAtIndex:stateRow];
    NSString *zip = [zips objectAtIndex:zipRow];
    
    NSString *title = [[NSString alloc] initWithFormat:@"You select 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];
}
从零开始学ios开发(十一):Tab Bars和Pickers

上面的code应该不需要过多的解释了吧,很简单,应该都可以看懂,接着添加viewDidLoad

从零开始学ios开发(十一):Tab Bars和Pickers
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSBundle *boundle = [NSBundle mainBundle];
    NSURL *plistURL = [boundle URLForResource:@"statedictionary" withExtension:@"plist"];
    
    NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:plistURL];
    
    self.stateZips = dictionary;
    
    NSArray *components = [self.stateZips allKeys];
    NSArray *sorted = [components sortedArrayUsingSelector:@selector(compare:)];
    self.states = sorted;
    NSString *selectedState = [self.states objectAtIndex:0];
    NSArray *array = [stateZips objectForKey:selectedState];
    self.zips = array;
}
从零开始学ios开发(十一):Tab Bars和Pickers

这里面的变化比较大,首先声明了一个NSBundle对象,这个对象在前几篇的例子中已经出现过(load一个xib),这里做一个比较详细的解释:bundle是一个特殊的文件夹,其中包含了程序会使用到的资源,这些资源包含了如图像、声音、编译好的代码、xib文件。对应bundle,cocoa提供了类NSBundle。

下面逐个对语句进行解释 NSBundle *bundle = [NSBundle mainBundle]; //这里的作用就是获取应用程序的主目录,然后在主目录中寻找需要的资源

NSURL *plistURL = [boundle URLForResource:@"statedictionary" withExtension:@"plist"]; //通过主目录来获取资源的路径(URL),这里寻找的资源是statedictionary.plist,有了资源的路径,就可以找到该资源

NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:plistURL]; //NSDictionary就是通过资源的路径来获取资源,并读取里面的数据

self.stateZips = dictionary; //将dictionary赋stateZips

NSArray *components = [self.stateZips allKeys]; //获取statedictionary.plist中所有的<key>

NSArray *sorted = [components sortedArrayUsingSelector:@selector(compare:)]; //将获取的所有<key>按字母序进行排列(这里我只知道是排序的意思,后面的方法我还没有深究,先这样子用着)

self.states = sorted; //将排完序的数组赋给states(左边的component)

NSString *selectedState = [self.states objectAtIndex:0]; // 当启动程序的时候,左边的component默认是选择第一个值的,这里获得第一个值,然后根据这个值找到对应的zips,然后显示在右边的component中

NSArray *array = [stateZips objectForKey:selectedState]; // 根据<key>来获得对应的zips

self.zips = array; //然后将数组赋给zips

继续写code

从零开始学ios开发(十一):Tab Bars和Pickers
- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    self.picker = nil;
    self.stateZips = nil;
    self.states = nil;
    self.zips = nil;
}
从零开始学ios开发(十一):Tab Bars和Pickers

不多解释了

从零开始学ios开发(十一):Tab Bars和Pickers
#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];
    
    return [self.zips count];
}
从零开始学ios开发(十一):Tab Bars和Pickers

Picker的DataSource方法,第一个方法返回2,说明有2个component,第二个方法返回component中数据的个数,这里以component做为区分,如果是kStateComponent,返回左边component数据的格式,否则返回右边component数据的个数。

从零开始学ios开发(十一):Tab Bars和Pickers
#pragma mark Picker Delegate Methods
- (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;
        [picker selectRow:0 inComponent:kZipComponent animated:YES];
        [picker reloadComponent:kZipComponent];
    }
}
从零开始学ios开发(十一):Tab Bars和Pickers

Picker的delegate方法,第一个方法还是和以前一样的,第二个方法是重点,用来更新右边component的数据,更新的方法和viewDidLoad中的方法是一样的,首先判断是不是左边的component发生了变化,如果是,得到左边component的当前值,根据这个值获取相应的zips,然后将zips赋值给右边的component,接着选中右边component的第一个值,并以动画的形式选中,滚动到第一个值,最后重新载入右边的component。

好了,以编译运行程序了,程序运行后,选择第四个tabitem 从零开始学ios开发(十一):Tab Bars和Pickers

试着改变左边component的选项,右边的zips会跟着改变 从零开始学ios开发(十一):Tab Bars和Pickers

程序似乎是做完了,但是并不完美,稍微往下滚动左边的component,会发现一些state的名字太长,没有完全显示出来,但是又发现右边的component有很多空白的地方被浪费了,因此最后能够拉长左边的component,缩短右边的component,方法很简单,在BIDDependentComponentPickerViewController.m中添加下面的方法 从零开始学ios开发(十一):Tab Bars和Pickers

- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
    if(component == kZipComponent)
        return 90;
    return 200;
}

该方法也是一个delegate,它用来设置pickerview中各个component的宽度,再次编译运行程序,这次程序界面完美了 从零开始学ios开发(十一):Tab Bars和Pickers

点击button,一个警告框弹出,告知你选择的内容 从零开始学ios开发(十一):Tab Bars和Pickers

20)Custom Picker Picker View中component所包含的对象不仅仅是string,也可以是其他,比如图片,在这个例子中,我们将做一个简单的游戏,类似于赌博机中的摇连续的“777”,一共会有5个component,每个component中包含5张不同的图片,然后根据随机数进行滚动,如果恰巧有连续3个component选中的图片一样,则获得胜利。

打开BIDCustomPickerViewController.h文件,添加如下code

从零开始学ios开发(十一):Tab Bars和Pickers
#import <UIKit/UIKit.h>

@interface BIDCustomPickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>

@property (strong, nonatomic) IBOutlet UIPickerView *picker;
@property (strong, nonatomic) IBOutlet UILabel *winLabel;
@property (strong, nonatomic) NSArray *column1;
@property (strong, nonatomic) NSArray *column2;
@property (strong, nonatomic) NSArray *column3;
@property (strong, nonatomic) NSArray *column4;
@property (strong, nonatomic) NSArray *column5;

- (IBAction)spin;

@end
从零开始学ios开发(十一):Tab Bars和Pickers

这里多声明了一个UILabel的outlet,当我们赢得一局后,label会显示“WIN”,否则label不显示,因此我们需要一个outlet来指向label。然后声明了5个NSArray,因为有5个component,所以需要5个NSArray与之对应。

打开BIDCustomPickerViewController.xib,拖一个Picker View放在view的顶部,选中Picker View,在Attributes inspector中找到Interaction,将User Interaction Enabled前的勾去掉,这样就不能用手指去滚动component了。 从零开始学ios开发(十一):Tab Bars和Pickers

然后拖一个Label放在Picker View下面,将Label的边框拉长到左右两边出现辅助线的位置,并在Attributes inspector中将Label的字体大小设置为48,颜色设为你喜欢的颜色即可,Label文字居中,在选中Label的状态下,使用快捷键command + =使Label边框的大小根据Label字体的大小进行改动。再拖一个button放在Label下面,并命名为Spin。 从零开始学ios开发(十一):Tab Bars和Pickers

接着就是连接outlet和action了,PickerView连接到picker outlet,Label连接到winLabel outlet,button连接到spin action,PickerView的dataSource和delegate连接到File‘s Owner。这些应该都很简单了,和之前的一样。

21)添加图片资源,继续写code 首先下载图片资源:CustomPickerImages,解压缩后拖入当Project navigator的Pickers目录下 从零开始学ios开发(十一):Tab Bars和Pickers 里面一共有6张png图片

打开BIDCustomPickerViewController.m文件,添加如下代码 

从零开始学ios开发(十一):Tab Bars和Pickers
#import "BIDCustomPickerViewController.h"

@implementation BIDCustomPickerViewController
@synthesize picker;
@synthesize winLabel;
@synthesize column1;
@synthesize column2;
@synthesize column3;
@synthesize column4;
@synthesize column5;

- (IBAction)spin
{
    BOOL win = NO;
    int numInRow = 1;
    int lastVal = -1;
    for (int i = 0; i < 5; i++) {
        int newValue = random() % [self.column1 count];
        
        if(newValue == lastVal)
            numInRow++;
        else
            numInRow = 1;
        
        lastVal = newValue;
        [picker selectRow:newValue inComponent:i animated:YES];
        [picker reloadComponent:i];
        if (numInRow >= 3) {
            win = YES;
        }
    }
    
    if (win)
        winLabel.text = @"WIN";
    else
        winLabel.text = @"";
}
从零开始学ios开发(十一):Tab Bars和Pickers

就解释一下spin方法中的for循环中

    for (int i = 0; i < 5; i++) {

        int newValue = random() % [self.column1 count]; //通过产生随机数,来确定component滚动到哪个位置,要对产生的随机数进行取余操作,否则很容易就超出component对象的个数,导致内存溢出报错。这里的余数表示component滚动到第几个位置

        if(newValue == lastVal) //相等时,说明随机数的余数和前一次的余数相等

            numInRow++; //计数器加1

        else

            numInRow = 1; //计数器重新置1,从新开始计数,最开始的时候计数器赋的值是-1,它不可能和任何余数相等,第一个component也不可能和任何其他的component进行对比,因此第一次执行for循环总是会跳到这里

        lastVal = newValue; //记录最近一次的余数,用于下次比较

        [picker selectRow:newValue inComponent:i animated:YES]; //设置picker中当前的component滚动到哪个位置

        [picker reloadComponent:i]; //重新载入当前的component

        if (numInRow >= 3) { //如果numInRow的值大于等于3,说明有连续3个相同的图标出现了

            win = YES; //连续出现3个相同的图标,赢得本局

        }

    }

继续添加viewDidLoad和viewDidUnload

从零开始学ios开发(十一):Tab Bars和Pickers
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    winLabel.text = @"";
    
    UIImage *seven = [UIImage imageNamed:@"seven.png"];
    UIImage *bar = [UIImage imageNamed:@"bar.png"];
    UIImage *crown = [UIImage imageNamed:@"crown.png"];
    UIImage *cherry = [UIImage imageNamed:@"cherry.png"];
    UIImage *lemon = [UIImage imageNamed:@"lemon.png"];
    UIImage *apple = [UIImage imageNamed:@"apple.png"];
    
    for (int i = 1; i <= 5; i++) {
        UIImageView *sevenView = [[UIImageView alloc] initWithImage:seven];
        UIImageView *barView = [[UIImageView alloc] initWithImage:bar];
        UIImageView *crownView = [[UIImageView alloc] initWithImage:crown];
        UIImageView *cherryView = [[UIImageView alloc] initWithImage:cherry];
        UIImageView *lemonView = [[UIImageView alloc] initWithImage:lemon];
        UIImageView *appleView = [[UIImageView alloc] initWithImage:apple];
        
        NSArray *imageViewArray = [[NSArray alloc] initWithObjects:sevenView, barView, crownView, cherryView, lemonView, appleView, nil];
        
        NSString *fieldName = [[NSString alloc] initWithFormat:@"column%d", i];
        [self setValue:imageViewArray forKey:fieldName];
    }
    
    srandom(time(NULL));

}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    self.picker = nil;
    self.winLabel = nil;
    self.column1 = nil;
    self.column2 = nil;
    self.column3 = nil;
    self.column4 = nil;
    self.column5 = nil;
}
从零开始学ios开发(十一):Tab Bars和Pickers

就解释一下viewDidLoad中某些语句(viewDidUnload很简单,就不做说明了):

UIImage *seven = [UIImage imageNamed:@"seven.png"]; //imageNamed方法是根据图片的名称去寻找图片并载入

UIImageView *sevenView = [[UIImageView alloc] initWithImage:seven]; //在for循环中根据UIImage对象创建6个UIImageView

NSArray *imageViewArray = [[NSArray alloc] initWithObjects:sevenView, barView, crownView, cherryView, lemonView, appleView, nil]; //创建一个Array,大家应该可以猜到,这个数组会赋给component,NSArray保存的对象是object,因此不论是string还是UIImageView,都可以保持在里面

NSString *fieldName = [[NSString alloc] initWithFormat:@"column%d", i]; //之前声明了5个NSArray,它们的名字都是有规律的column*

[self setValue:imageViewArray forKey:fieldName]; //setValue:forKey:是根据属性(property)的名字来为其赋值,我们使用这个方法,来为5个column*赋值

srandom(time(NULL)); //设置随机数的因子,可以是每次产生的随机数不同

最后添加dataSource和delegate方法

从零开始学ios开发(十一):Tab Bars和Pickers
#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
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    NSString *arrayName = [[NSString alloc] initWithFormat:@"column%d", component + 1];
    NSArray *array = [self valueForKey:arrayName];
    return [array objectAtIndex:row];
}
从零开始学ios开发(十一):Tab Bars和Pickers

DataSource方法和之前的并无两样,就不解释了,而delegate方法则稍稍有些不同,之前返回的董事NSString,这里返回的UIView,因为component中的对象是UIImageView

NSString *arrayName = [[NSString alloc] initWithFormat:@"column%d", component + 1]; //参数component传过来的值是0~4,所以必须要加1,才能对应到column1~column5

NSArray *array = [self valueForKey:arrayName]; //valueForKey根据property的名字得到对象

return [array objectAtIndex:row]; //根据得到的array返回第row个对象

22)编译运行 选择第五个tabitem,程序的初始界面 从零开始学ios开发(十一):Tab Bars和Pickers

点击多次Spin后,终于获胜 从零开始学ios开发(十一):Tab Bars和Pickers

23)总结 这篇文章主要讲解了TabBar和PickerView的使用,TabBar作为root controller,控制了5个subview的切换,PickerView有2种,一种是DatePicker,另一种是PickerView,DatePicker专门处理日期,比较简单。PickerView相对复杂一些,需要引入<UIPickerViewDelegate, UIPickerViewDataSource>,并使用其中的方法告知PickerView中有多少个component和每个component中有多少个对象,还要绑定NSArray,并自己写返回对象的方法。

24)一个小问题 如果你和我使用的是同一个版本的xcode 从零开始学ios开发(十一):Tab Bars和Pickers

那在编译项目的时候,始终会有一个感叹号存在 从零开始学ios开发(十一):Tab Bars和Pickers

TabBarController.xib: warning: Attribute Unavailable: Defines Presentation Context is not available prior to Xcode 4.2.

我也不太清楚是什么意思,但是查了一下网,选中TabBarController.xib,在Attributes inspector中,把Defines Context前的勾去掉就可以,然后编译就没有感叹号了。 从零开始学ios开发(十一):Tab Bars和Pickers

 

Pickers

从零开始学ios开发(十一):Tab Bars和Pickers,布布扣,bubuko.com

从零开始学ios开发(十一):Tab Bars和Pickers

上一篇:IOS控件 UIDatePicker


下一篇:android拦截短信并屏蔽系统的Notification