选择器UIPickerView

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所示的效果。

1C77CD48-E4A5-4557-A243-A0751D01CBCE.png

图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所示的两列选择器。
A6CEE31D-3D1D-42EB-B3C5-CDAC9BC0001A.png

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所示的效果。
9AE4D06D-F3BB-4E9D-8306-369DBCBD1160.png

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;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容