寫在文前
由于最近開發(fā)中經(jīng)常碰到類似日期選擇器相關(guān)業(yè)務(wù)使用場(chǎng)景,雖然這個(gè)系統(tǒng)控件相對(duì)來說非常簡(jiǎn)單,有點(diǎn)兒類似UITableView的感覺,初始化之后設(shè)置數(shù)據(jù)源,代理,完成相應(yīng)的數(shù)據(jù)源方法就可以正常展示了,而且其數(shù)據(jù)源 代理方法相對(duì)來說也很少,肯花心思去思考 記憶,很快就能掌握這個(gè)控件。
一、UIPickerView 簡(jiǎn)介
UIPickerView 是一種使用旋轉(zhuǎn)輪或一個(gè)槽機(jī)映像來顯示一列或多列,可多行展示的滾動(dòng)視圖。 其與UIDatePicker 展示效果極為相似, 但是其是一個(gè)相對(duì)開發(fā)者來說更為通用的滾動(dòng)選擇器。 由類名就可以得出UIDatePicker 是為日期選擇而生, 而其是一個(gè)通用適合自定義視圖展示的選擇器。
UIPickerView 繼承自 UIView。 UIDatePicker繼承自Control。
兩者不同的父類也直接導(dǎo)致了 UIPickerView 相對(duì) UIDatePicker 也缺少了很多 Control 自帶的特性。畢竟 Control是從 UIView 繼承出來的。

二、UIPickerView 相應(yīng)屬性與方法
在此我以系統(tǒng)聲明的 UIPickerView.h 文件聲明的屬性及方法依次介紹, 其實(shí)文檔上已經(jīng)寫的挺清楚了,但是在使用的時(shí)候可能也會(huì)由于理解錯(cuò)誤,介紹不夠詳細(xì)等導(dǎo)致踩坑,所以我會(huì)按系統(tǒng)文檔加上自己在使用過程中的理解心得結(jié)合起來介紹。
為了便于觀看直接把系統(tǒng) 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 數(shù)據(jù)源初始化的時(shí)候可以直接設(shè)置。
@property(nullable,nonatomic,weak) id<UIPickerViewDelegate> delegate; // default is nil. weak reference 委托初始化的時(shí)候可以直接設(shè)置。
@property(nonatomic) BOOL showsSelectionIndicator; // default is NO
很遺憾,這個(gè)屬性在 iOS7 以及更高的系統(tǒng)就無法使用了,Apple 文檔里面有詳細(xì)介紹。
查了下資料解釋說其顯示的效果就是在當(dāng)前選擇的行中 有一個(gè)默認(rèn)背景顏色填充的藍(lán)條。
光看著描述,不能看到效果有點(diǎn)難受QAQ, 直接Google 這個(gè)屬性找到不少相關(guān)圖片,嘿嘿嘿。
效果: 雖然系統(tǒng)屏蔽掉了這個(gè)直接設(shè)置已選列表指示器的接口。不過我們通過現(xiàn)有的公共方法實(shí)現(xiàn)起來也非常簡(jiǎn)單,直接在代理的已選中當(dāng)前 Row 中通過返回當(dāng)前 Row 的View 方法,直接設(shè)置背景即可。

showsSelectionIndicator 實(shí)現(xiàn)代碼:
- (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
// 這三個(gè)方法可以直接獲取到當(dāng)前 UIPickerView 行數(shù),列數(shù),每列的展示行相應(yīng)大小數(shù)據(jù)。
@property(nonatomic,readonly) NSInteger numberOfComponents; // 可直接獲取當(dāng)前選擇器的列個(gè)數(shù)。
- (NSInteger)numberOfRowsInComponent:(NSInteger)component; // 參數(shù)代表當(dāng)前列下標(biāo)。直接返回當(dāng)前列總共包含多少行組件。
- (CGSize)rowSizeForComponent:(NSInteger)component; // 參入指定列下標(biāo),返回當(dāng)前所展示單行視圖的寬度和高度。
// 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 視圖。 但是要主要這個(gè)方法是通過 代理方法此 pickerView:viewForRow:forComponent:reusingView:
// 獲取到的 View。 所以如果我們沒有實(shí)現(xiàn)此代理方法的話,調(diào)用則返回 nil。
// Reloading whole view or single component
- (void)reloadAllComponents; // 刷新整個(gè)選擇控制器。 類似 UITableView 里的 - (void)reloadData;
- (void)reloadComponent:(NSInteger)component; // 傳入列下標(biāo),指定刷新這一列數(shù)據(jù)。
// 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.
// 傳入行和列下標(biāo),選擇控制器會(huì)滾動(dòng)到相應(yīng)視圖, 并使其展示在中間。
- (NSInteger)selectedRowInComponent:(NSInteger)component; // returns selected row. -1 if nothing selected
// 傳入當(dāng)前列下標(biāo),返回當(dāng)前選擇控制器當(dāng)前所選中的 Row 下標(biāo)。
@end
數(shù)據(jù)源
//只有兩個(gè)必須實(shí)現(xiàn)的方法。
__TVOS_PROHIBITED
@protocol UIPickerViewDataSource<NSObject>
@required
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView; //參數(shù)表示當(dāng)前 pickerView,返回選擇器總共有幾列。
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
//參數(shù) pickerView 表示當(dāng)前 pickerView 視圖,component 表示所在列下標(biāo), 返回指定列下標(biāo)的行數(shù)。
@end
代理
// 6個(gè)方法都是非常好理解的。
__TVOS_PROHIBITED
@protocol UIPickerViewDelegate<NSObject>
@optional
// returns width of column and height of row for each component.
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
// 傳入當(dāng)前列下標(biāo),返回值為此行的寬度。 可以通過這個(gè)方法來單獨(dú)設(shè)置每一列的寬度。
__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
// 這三個(gè)方法都是決定 UIPickerVier 展示的內(nèi)容,效果。 如果對(duì)視圖沒有定義要求的話,直接使用前面兩個(gè)即可。
// 第三個(gè)方法是可以自定義視圖進(jìn)行展示的,并且其原理也是類似UITableView一樣用復(fù)用的功能。
// 細(xì)節(jié):這三個(gè)方法 第一個(gè) 和 第二個(gè) 如果我們同時(shí) 實(shí)現(xiàn)了,則系統(tǒng)會(huì)先調(diào)用返回 NSAttributedString 的方法,其次在去調(diào)用 返回 NSString 的方法。
// 但是如果我們實(shí)現(xiàn)了 返回 UIView 的方法的話, 其他兩個(gè)方法均不會(huì)在被調(diào)用。 這點(diǎn)要注意一下。
- (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;
// 這個(gè)方法顧名思義,在用戶滑動(dòng) 選擇器列表的時(shí)候會(huì)調(diào)用此方法。 類似UITableView里面的 將要選擇那一分區(qū)里面那一行一樣的!
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component __TVOS_PROHIBITED;
@end
NS_ASSUME_NONNULL_END
到這里已經(jīng)基本把 UIPickerView 相關(guān)的所有屬性及方法介紹完畢。 相信沒有使用過這個(gè)控件的朋友基本也差不多明白是怎么回事了。
這里我再附上簡(jiǎn)單的代碼實(shí)現(xiàn)來供大家參考。
// pickView初始化并設(shè)置其大小,如果不設(shè)置其大小,默認(rèn)大小為 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";
}
效果如圖:

也可以通過另外兩個(gè)代理方法來實(shí)現(xiàn) UIPickerView 的展示。 我一般比較喜歡用復(fù)用 View 的方法。不僅能節(jié)省內(nèi)存開銷,自定義修改視圖字體等都很輕松的實(shí)現(xiàn)。
- (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;
}
如果我們使用這種方法來實(shí)現(xiàn) UIPickerView 的展現(xiàn)的話,還有一個(gè)好處就是可以通過 調(diào)用 UIPickerView 的
- (nullable UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component;
方法就能輕松實(shí)現(xiàn) 其 iOS 7 以后就隱藏掉的屬性 showsSelectionIndicator。
具體實(shí)現(xiàn)如下:
在實(shí)現(xiàn)了上面的復(fù)用自定義視圖代理方法基礎(chǔ)上,調(diào)用選擇指定行列下標(biāo)代理方法。
- (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];
}
效果如圖:

到這里 UIPickerView 的介紹基本結(jié)束了,想必閱讀下來大家肯定都能動(dòng)手自己寫一個(gè)適合自己業(yè)務(wù)場(chǎng)景使用的 UIPickerView了。
補(bǔ)充個(gè)實(shí)用的Tips
一、UIPickerView 默認(rèn)是會(huì)顯示在中心的 視圖上下分割線, 有時(shí)候我們想去去掉這條分割線,或者改變顏色可以使用這個(gè)方法。
//清除或改變分割線的顏色等。
for(UIView *singleLine in _pickerView.subviews)
{
if (singleLine.frame.size.height < 1)
{
singleLine.backgroundColor = [UIColor clearColor];
[singleLine removeFromSuperview];
}
}
最后
本文參考了很多前輩的文章及開發(fā)中自己總結(jié)的結(jié)論,希望此篇文章對(duì)您有所幫助,如有不對(duì)的地方,希望大家能留言指出糾正。歡迎大家一起交流學(xué)習(xí) 澤西島上咖啡?。。。?!