iOS-UIPickerView 详解总结

写在文前

由于最近开发中经常碰到类似日期选择器相关业务使用场景,虽然这个系统控件相对来说非常简单,有点儿类似UITableView的感觉,初始化之后设置数据源,代理,完成相应的数据源方法就可以正常展示了,而且其数据源 代理方法相对来说也很少,肯花心思去思考 记忆,很快就能掌握这个控件。

一、UIPickerView 简介

UIPickerView 是一种使用旋转轮或一个槽机映像来显示一列或多列,可多行展示的滚动视图。 其与UIDatePicker 展示效果极为相似, 但是其是一个相对开发者来说更为通用的滚动选择器。 由类名就可以得出UIDatePicker 是为日期选择而生, 而其是一个通用适合自定义视图展示的选择器。
UIPickerView 继承自 UIView。 UIDatePicker继承自Control。
两者不同的父类也直接导致了 UIPickerView 相对 UIDatePicker 也缺少了很多 Control 自带的特性。毕竟 Control是从 UIView 继承出来的。


UIPickerView

二、UIPickerView 相应属性与方法

在此我以系统声明的 UIPickerView.h 文件声明的属性及方法依次介绍, 其实文档上已经写的挺清楚了,但是在使用的时候可能也会由于理解错误,介绍不够详细等导致踩坑,所以我会按系统文档加上自己在使用过程中的理解心得结合起来介绍。

为了便于观看直接把系统 UIPickerView.h 文件代码 Copy 过来。 直接在属性原有注释上添加自己的理解介绍。
//
//  UIPickerView.h
//  UIKit
//
//  Copyright (c) 2006-2017 Apple Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <UIKit/UIView.h>
#import <UIKit/UIKitDefines.h>

NS_ASSUME_NONNULL_BEGIN

@protocol UIPickerViewDataSource, UIPickerViewDelegate;

NS_CLASS_AVAILABLE_IOS(2_0) __TVOS_PROHIBITED @interface UIPickerView : UIView <NSCoding>

@property(nullable,nonatomic,weak) id<UIPickerViewDataSource> dataSource; // default is nil. weak reference  数据源初始化的时候可以直接设置。
@property(nullable,nonatomic,weak) id<UIPickerViewDelegate>   delegate; // default is nil. weak reference  委托初始化的时候可以直接设置。
@property(nonatomic)        BOOL    showsSelectionIndicator;   // default is NO    
很遗憾,这个属性在 iOS7 以及更高的系统就无法使用了,Apple 文档里面有详细介绍。   
查了下资料解释说其显示的效果就是在当前选择的行中 有一个默认背景颜色填充的蓝条。
光看着描述,不能看到效果有点难受QAQ, 直接Google 这个属性找到不少相关图片,嘿嘿嘿。

效果: 虽然系统屏蔽掉了这个直接设置已选列表指示器的接口。不过我们通过现有的公共方法实现起来也非常简单,直接在代理的已选中当前 Row 中通过返回当前 Row 的View 方法,直接设置背景即可。

UIPickerView.showsSelecttionIndicator = Yes

showsSelectionIndicator 实现代码:

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{ 
      UIView* selectView = [pickerView viewForRow:row forComponent:component];
      selectView.backgroundColor = [UIColor redColor];
}
// info that was fetched and cached from the data source and delegate
// Getting the Dimensions of the View Picker
// 这三个方法可以直接获取到当前 UIPickerView 行数,列数,每列的展示行相应大小数据。
@property(nonatomic,readonly) NSInteger numberOfComponents;   // 可直接获取当前选择器的列个数。
- (NSInteger)numberOfRowsInComponent:(NSInteger)component;   //  参数代表当前列下标。直接返回当前列总共包含多少行组件。
- (CGSize)rowSizeForComponent:(NSInteger)component;  //   参入指定列下标,返回当前所展示单行视图的宽度和高度。

// returns the view provided by the delegate via pickerView:viewForRow:forComponent:reusingView:
// or nil if the row/component is not visible or the delegate does not implement 
// pickerView:viewForRow:forComponent:reusingView:
- (nullable UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component;
// 此方法是传入指定行和列 返回其表示的 View 视图。  但是要主要这个方法是通过 代理方法此 pickerView:viewForRow:forComponent:reusingView:
// 获取到的 View。  所以如果我们没有实现此代理方法的话,调用则返回 nil。
// Reloading whole view or single component
- (void)reloadAllComponents;      // 刷新整个选择控制器。 类似 UITableView 里的 - (void)reloadData;
- (void)reloadComponent:(NSInteger)component; // 传入列下标,指定刷新这一列数据。

// selection. in this case, it means showing the appropriate row in the middle
- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated;  // scrolls the specified row to center.
// 传入行和列下标,选择控制器会滚动到相应视图, 并使其展示在中间。

- (NSInteger)selectedRowInComponent:(NSInteger)component;                                   // returns selected row. -1 if nothing selected
//  传入当前列下标,返回当前选择控制器当前所选中的 Row 下标。

@end

数据源

//只有两个必须实现的方法。
__TVOS_PROHIBITED
@protocol UIPickerViewDataSource<NSObject>
@required

// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView; //参数表示当前 pickerView,返回选择器总共有几列。

// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
//参数 pickerView 表示当前 pickerView 视图,component 表示所在列下标, 返回指定列下标的行数。
@end

代理

// 6个方法都是非常好理解的。

__TVOS_PROHIBITED
@protocol UIPickerViewDelegate<NSObject>
@optional

// returns width of column and height of row for each component. 
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component 
//   传入当前列下标,返回值为此行的宽度。 可以通过这个方法来单独设置每一列的宽度。
__TVOS_PROHIBITED;
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component 
//   同上方法,这里是返回高度。
__TVOS_PROHIBITED;

// these methods return either a plain NSString, a NSAttributedString, or a view (e.g UILabel) to display the row for the component.
// for the view versions, we cache any hidden and thus unused views and pass them back for reuse. 
// If you return back a different object, the old one will be released. the view will be centered in the row rect  
// 这三个方法都是决定 UIPickerVier 展示的内容,效果。  如果对视图没有定义要求的话,直接使用前面两个即可。 
// 第三个方法是可以自定义视图进行展示的,并且其原理也是类似UITableView一样用复用的功能。
// 细节:这三个方法 第一个 和 第二个 如果我们同时 实现了,则系统会先调用返回 NSAttributedString 的方法,其次在去调用 返回 NSString 的方法。  
// 但是如果我们实现了 返回 UIView 的方法的话, 其他两个方法均不会在被调用。 这点要注意一下。
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component __TVOS_PROHIBITED;
- (nullable NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED; // attributed title is favored if both methods are implemented
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view __TVOS_PROHIBITED;

//  这个方法顾名思义,在用户滑动 选择器列表的时候会调用此方法。  类似UITableView里面的 将要选择那一分区里面那一行一样的!
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component __TVOS_PROHIBITED;

@end

NS_ASSUME_NONNULL_END

到这里已经基本把 UIPickerView 相关的所有属性及方法介绍完毕。 相信没有使用过这个控件的朋友基本也差不多明白是怎么回事了。
这里我再附上简单的代码实现来供大家参考。

// pickView初始化并设置其大小,如果不设置其大小,默认大小为 320 * 216。
- (void)viewDidLoad {
 [super viewDidLoad];

 UIPickerView* pickerView = [[UIPickerView alloc] initWithFrame:self.view.frame];
 pickerView.delegate = self;
 pickerView.dataSource = self;
 [self.view addSubview:pickerView];
}


#pragma mark - UIPickerView DataSource and Delegate 

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

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
 return self.dataSource.count;
}

- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
 return 46;
}

- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return @"JerseyCocoa";
}

效果如图:

Show-UIPickerView
也可以通过另外两个代理方法来实现 UIPickerView 的展示。 我一般比较喜欢用复用 View 的方法。不仅能节省内存开销,自定义修改视图字体等都很轻松的实现。
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
    UILabel* pickerLabel = (UILabel*)view;
    if (!pickerLabel) {
        pickerLabel = [[UILabel alloc] init];
        pickerLabel.font = [UIFont systemFontOfSize:15];
        pickerLabel.contentMode = UIViewContentModeCenter;
        pickerLabel.textColor = [UIColor blueColor];
    }
    pickerLabel.text = [self pickerView:pickerView titleForRow:row forComponent:component];
    return pickerLabel;
}
如果我们使用这种方法来实现 UIPickerView 的展现的话,还有一个好处就是可以通过 调用 UIPickerView 的
 - (nullable UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component; 
方法就能轻松实现 其 iOS 7 以后就隐藏掉的属性  showsSelectionIndicator。
具体实现如下:
在实现了上面的复用自定义视图代理方法基础上,调用选择指定行列下标代理方法。
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    UIView* view = [self pickerView:pickerView viewForRow:row forComponent:component reusingView:nil];
    view.backgroundColor = [UIColor greenColor];
}
效果如图:
pickerView.showsSelectionIndicator = Yes

到这里 UIPickerView 的介绍基本结束了,想必阅读下来大家肯定都能动手自己写一个适合自己业务场景使用的 UIPickerView了。

补充个实用的Tips

一、UIPickerView 默认是会显示在中心的 视图上下分割线, 有时候我们想去去掉这条分割线,或者改变颜色可以使用这个方法。

    //清除或改变分割线的颜色等。
    for(UIView *singleLine in _pickerView.subviews)
    {
        if (singleLine.frame.size.height < 1)
        {
            singleLine.backgroundColor = [UIColor clearColor];
            [singleLine removeFromSuperview];
        }
    }

最后

本文参考了很多前辈的文章及开发中自己总结的结论,希望此篇文章对您有所帮助,如有不对的地方,希望大家能留言指出纠正。欢迎大家一起交流学习 泽西岛上咖啡!!!!!

学习的路上, 与君共勉!!!

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

推荐阅读更多精彩内容