UIPickerView也是一个选择器控件,它比UIDatePicker更加通用,它可以生成单列的选择器,也可生成多列的选择器,而且开发者完全可以自定义选择项的外观,因此用法非常灵活。
UIPickerView直接继承了UIView,没有继承UIControl,因此,它不能像UIControl那样绑定事件处理方法,UIPickerView的事件处理由其委托对象完成。
UIPickerView控件常用的属性和方法如下。
- numberOfComponents:获取UIPickerView指定列中包含的列的数量。该属性是一个只读属性。
- showsSelectionIndicator:该属性控制是否显示UIPickerView中的选中标记(以高亮背景作为选中标记)。
- numberOfRowsInComponent::获取UIPickerView包含的行数量。
- rowSizeForComponent::获取UIPickerView包含的指定列中列表项的大小。该方法返回一个CGSize对象。
- selectRow:inComponent:animated::该方法设置选中该UIPickerView中指定列的特定列表项。最后一个参数控制是否使用动画。
- selectedRowInComponent::该方法返回该UIPickerView指定列中被选中的列表项。
- viewForRow:forComponent::该方法返回该UIPickerView指定列的列表项所使用的UIView控件。
UIDatePicker控件只是负责该控件的通用行为,而该控件包含多少列,各列包含多少个列表项则由UIPickerViewDataSource对象负责。开发者必须为UIPickerView设置UIPickerViewDataSource对象,并实现如下两个方法。
- numberOfComponentsInPickerView::该UIPickerView将通过该方法来判断应该包含多少列。
- pickerView:numberOfRowsInComponent::该UIPickerView将通过该方法判断指定列应该包含多少行列表项。
如果程序需要控制UIPickerView中各列的宽度,以及各列中列表项的大小和外观,或程序需要为UIPickerView的选中事件提供响应,都需要为UIPickerView设置UIPickerViewDelegate委托对象,并根据需要实现该委托对象中的如下方法。
- pickerView:rowHeightForComponent::该方法返回的CGFloat值将作为该UIPickerView控件中指定列中列表项的高度。
- pickerView:widthForComponent::该方法返回的CGFloat值将作为该UIPickerView控件中指定列的宽度。
- pickerView:titleForRow:forComponent::该方法返回的NSString值将作为该UIPickerView控件中指定列的列表项的文本标题。
- pickerView:viewForRow:forComponent:reusingView::该方法返回的UIView控件将直接作为该UIPickerView控件中指定列的指定列表项。可以在这边自定义列表项
- pickerView:didSelectRow:inComponent::当用户单击选中该UIPickerView控件的指定列的指定列表项时将会激发该方法。
UIPickerView 的功能和用法
1.单列选择器
对于单列选择器,只要控制UIPickerView的dataSource对象的numberOfComponentsInPickerView:方法返回1即可。
下面创建一个单列选择器,首先创建一个Single View Application,使用Interface Builder打开应用的界面设计文件,并将一个UIPickerView拖入界面设计文件中,为在程序中访问该控件,需要将该控件绑定到picker IBOutlet属性。
接下来开始修改控制器类,本例打算使用控制器类作为UIPickerView的dataSource和delegate,因此,程序需要让控制器类实现UIPickerViewDataSource、UIPickerViewDelegate两个协议。
修改控制器类的实现代码,主要就是实现UIPickerViewDataSource、UIPickerViewDelegate两个协议中的必要方法,其代码如下。
NSArray* books;
- (void)viewDidLoad
{
[super viewDidLoad];
// 创建并初始化NSArray对象
books = [NSArray arrayWithObjects:@"疯狂Android讲义",
@"疯狂iOS讲义", @"疯狂Ajax讲义" , @"疯狂XML讲义", nil];
// 为UIPickerView控件设置dataSource和delegate
self.picker.dataSource = self;
self.picker.delegate = self;
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件包含多少列 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
return 1; // 返回1表明该控件只包含1列
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件指定列包含多少个列表项 - (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
// 由于该控件只包含一列,因此无须理会列序号参数component
// 该方法返回books.count,表明books包含多少个元素,该控件就包含多少行
return books.count;
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为UIPickerView
// 中指定列和列表项的标题文本 - (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
// 由于该控件只包含一列,因此无须理会列序号参数component
// 该方法根据row参数返回books中的元素,row参数代表列表项的编号,
// 因此该方法表示第几个列表项,就使用books中的第几个元素
return [books objectAtIndex:row];
}
// 当用户选中UIPickerViewDataSource中指定列和列表项时激发该方法 - (void)pickerView:(UIPickerView )pickerView didSelectRow:
(NSInteger)row inComponent:(NSInteger)component
{
// 使用一个UIAlertView来显示用户选中的列表项
UIAlertView alert = [[UIAlertView alloc]
initWithTitle:@"提示"
message:[NSString stringWithFormat:@"你选中的图书是:%@"
, [books objectAtIndex:row]]
delegate:nil
cancelButtonTitle:@"确定"
otherButtonTitles:nil];
[alert show];
}
@end
上面的程序首先初始化了一个NSArray,接下来的关键就是实现了4个用粗体字表示的方法,其中有两个方法来自UIPickerViewDataSource协议,分别用于控制该UIPickerView控件包含多少列、各列包含多少个列表项;另两个方法则来自UIPickerViewDelegate,最后一个粗体字方法负责为UIPickerView控件的选中事件提供响应——当用户选中该UIPickerView的某个列表项时,系统将会自动激发该方法,该方法的实现逻辑就是使用UIAlertView显示用户选择的图书。
编译、运行该程序,可以看到如图10.46所示的效果。
图10.46 单列UIPickerView
2.多列选择器
对单列选择器而言,只要控制UIPickerView的dataSource对象的numberOfComponentsInPickerView:方法返回大于1的整数即可。
本节创建一个多列选择器,首先创建一个Single View Application,使用Interface Builder打开应用的界面设计文件,并将一个UIPickerView拖入界面设计文件中,为在程序中访问该控件,需要将该控件绑定到picker IBOutlet属性。
接下来修改控制器类,本例打算使用控制器类作为UIPickerView的dataSource和delegate,因此程序需要让控制器类实现UIPickerViewDataSource、UIPickerViewDelegate两个协议。
修改控制器类的实现代码,主要就是实现UIPickerViewDataSource、UIPickerViewDelegate两个协议中的必要方法,其代码如下。
NSArray* books;
NSArray* authors;
- (void)viewDidLoad
{
[super viewDidLoad];
// 创建并初始化两个NSArray对象,分别作为两列的数据
authors = [NSArray arrayWithObjects:@"泰戈尔",
@"冯梦龙", @"李刚" , nil];
books = [NSArray arrayWithObjects:@"飞鸟集" , @"吉檀迦利"
, @"醒世恒言",@"喻世明言", @"警世通言", @"疯狂Android讲义",
@"疯狂iOS讲义", @"疯狂Ajax讲义" , @"疯狂XML讲义", nil];
self.picker.dataSource = self;
self.picker.delegate = self;
}
// UIPickerViewDataSource中定义的方法,该方法返回值决定该控件包含多少列 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
return 2; // 返回2表明该控件只包含2列
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件指定列包含多少个列表项 - (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
// 如果是第一列,返回authors中元素的个数
// 即authors包含多少个元素,第一列就包含多少个列表项
if (component == 0) {
return authors.count;
}
// 如果是其他列,返回books中元素的个数。
// 即books包含多少个元素,其他列(只有第二列)包含多少个列表项
return books.count;
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列和列表项上显示的标题 - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:
(NSInteger)row forComponent:(NSInteger)component
{
// 如果是第一列,返回authors中row索引处的元素
// 即第一列的列表项标题由authors集合元素决定
if (component == 0) {
return [authors objectAtIndex:row];
}
// 如果是其他列(只有第二列),返回books中row索引处的元素
// 即第二列的列表项标题由books集合元素决定
return [books objectAtIndex:row];
}
// 当用户选中UIPickerViewDataSource中指定列和列表项时激发该方法 - (void)pickerView:(UIPickerView )pickerView didSelectRow:
(NSInteger)row inComponent:(NSInteger)component
{
NSArray tmp = component == 0 ? authors: books;
NSString* tip = component == 0 ? @"作者": @"图书";
// 使用一个UIAlertView来显示用户选中的列表项
UIAlertView* alert = [[UIAlertView alloc]
initWithTitle:@"提示"
message:[NSString stringWithFormat:@"你选中的%@是:%@,"
, tip, [tmp objectAtIndex:row]]
delegate:nil
cancelButtonTitle:@"确定"
otherButtonTitles:nil];
[alert show];
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列的宽度 - (CGFloat)pickerView:(UIPickerView *)pickerView
widthForComponent:(NSInteger)component
{
// 如果是第一列,宽度为90
if (component == 0) {
return 90;
}
return 210; // 如果是其他列(只有第二列),宽度为210
}
@end
该程序与前一个程序基本相似,同样需要实现上面4个方法,只是numberOfComponentsInPickerView:方法返回了2,因此,该UIPickerView包含两列,程序创建了两个NSArray,分别为两列提供数据项。除此之外,上面的程序还实现了一个pickerView:widthForComponent:方法,该方法的返回值控制UIPickerView中各列的宽度。
编译、运行该程序,可以看到如图10.48所示的两列选择器。
3.相互依赖的多列选择器
弟2节的示例虽然是一个两列的UIPickerView控件,但该控件的两列基本没有任何关系,选择第一列的作者时,第二列的图书不会动态更新——在某些情况下,这是允许的。但在某些情况下,我们需要第二列的列表项依赖第一列的选择,即当第一列选择作者时,第二列只显示该作者的图书。
为了让第二列能根据第一列的选择动态加载,程序需要用户选择第一列的事件,并根据该事件动态加载第二列的数据,然后强制重新加载UIDatePicker的第二列列表项。
下面创建一个相互依赖的多列选择器,首先创建一个Single View Application,使用Interface Builder打开应用的界面设计文件,并将一个UIPickerView拖入界面设计文件中,为在程序中访问该控件,需要将该控件绑定到picker IBOutlet属性。
接下来开始修改控制器类,本例使用控制器类作为UIPickerView的dataSource和delegate,因此,程序需要让控制器类实现UIPickerViewDataSource、UIPickerViewDelegate两个协议。
修改控制器类的实现代码,主要就是实现UIPickerViewDataSource、UIPickerViewDelegate两个协议中的必要方法,其代码如下。
NSDictionary* books;
NSArray* authors;
// selectedAuthor保存当前选中的作者
NSString* selectedAuthor;
- (void)viewDidLoad
{
[super viewDidLoad];
// 创建并初始化NSDictionary对象
books = [NSDictionary dictionaryWithObjectsAndKeys:
[NSArray arrayWithObjects:@"飞鸟集" , @"吉檀迦利", nil]
, @"泰戈尔",
[NSArray arrayWithObjects:@"醒世恒言",@"喻世明言"
, @"警世通言", nil] , @"冯梦龙",
[NSArray arrayWithObjects:@"疯狂Android讲义",
@"疯狂iOS讲义", @"疯狂Ajax讲义" , @"疯狂XML讲义", nil]
, @"李刚" ,nil];
// 使用authors保存books所有key组成的NSArray排序后的结果
authors = [[books allKeys] sortedArrayUsingSelector:
@selector(compare:)];
// 设置默认选中的作者authors中的第一个元素
selectedAuthor = [authors objectAtIndex:0];
self.picker.dataSource = self;
self.picker.delegate = self;
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件包含多少列 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
return 2; // 返回2表明该控件只包含2列
}
// UIPickerViewDataSource中定义的方法,该方法的返回值决定该控件指定列包含多少个列表项 - (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
// 如果是第一列,返回authors中元素的个数
// 即authors包含多少个元素,第一列包含多少个列表项
if (component == 0) {
return authors.count;
}
// 如果是其他列(只有第二列),
// 返回books中selectedAuthor对应的NSArray中元素的个数
return [[books objectForKey:selectedAuthor] count];
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列和列表项上显示的标题 - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:
(NSInteger)row forComponent:(NSInteger)component
{
// 如果是第一列,返回authors中row索引处的元素
// 即第一列的元素由authors集合元素决定
if (component == 0) {
return [authors objectAtIndex:row];
}
// 如果是其他列(只有第二列),
// 返回books中selectedAuthor对应的NSArray中row索引处的元素
return [[books objectForKey:selectedAuthor] objectAtIndex:row];
}
// 当用户选中UIPickerViewDataSource中指定列和列表项时激发该方法 - (void)pickerView:(UIPickerView )pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
if(component == 0)
{
// 改变被选中的作者
selectedAuthor = [authors objectAtIndex:row];
// 控制重写加载第二个列表,根据选中的作者来加载第二个列表
[self.picker reloadComponent:1];
}
NSArray tmp = component == 0 ? authors:
[books objectForKey:selectedAuthor];
NSString* tip = component == 0 ? @"作者": @"图书";
// 使用一个UIAlertView来显示用户选中的列表项
UIAlertView* alert = [[UIAlertView alloc]
initWithTitle:@"提示"
message:[NSString stringWithFormat:@"你选中的%@是:%@,"
, tip , [tmp objectAtIndex:row]]
delegate:nil
cancelButtonTitle:@"确定"
otherButtonTitles:nil];
[alert show];
}
// UIPickerViewDelegate中定义的方法,该方法返回的NSString将作为
// UIPickerView中指定列的宽度 - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:
(NSInteger)component
{
// 如果是第一列,宽度为90
if (component == 0) {
return 90;
}
return 210; // 如果是其他列(只有第二列),宽度为210
}
@end
上面控制器类的实现部分与前一个示例中控制器类的实现部分大致相同,关键就是程序中的粗体字代码。本程序采用了NSDictionary分别保存NSPickerView控件中的两列数据,NSDictionary的所有key组成的集合作为第1列的数据,当用户选中第一列的某个作者后,程序取出NSDictionary中该作者对应的图书集合作为第二列的数据。这就可以让第二列数据随第一列的选择动态改变。
编译、运行该程序,在第一列中选中某个作者,可以看到如图10.49所示的效果。
4.自定义选择器视图
前面示例看到的所有列表项都是文字形式,实际上,UIPickerView允许开发者对列表项进行任意定制。开发者只要实现UIPickerViewDelegate协议中的-pickerView:viewForRow:forComponent: reusingView:方法即可,该方法返回的UIView将作为UIPickerView指定列和列表项的视图控件。
如下:
// UIPickerViewDelegate中定义的方法,该方法返回的UIView将作为
// UIPickerView中指定列和列表项的UI控件
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:
(NSInteger)row forComponent:(NSInteger)component
reusingView:(UIView *)view
{
// 如果可重用的view的tag不等于kImageTag,表明该view已经不存在,需要重新创建
if(view.tag != kImageTag)
{
view = [[UIImageView alloc] initWithImage:[images objectAtIndex:row]];
// 为该UIView设置tag属性
view.tag = kImageTag;
// 设置不允许用户交互
view.userInteractionEnabled = NO;
}
return view;
}