寫在文前
由于最近開發中經常碰到類似日期選擇器相關業務使用場景,雖然這個系統控件相對來說非常簡單,有點兒類似UITableView的感覺,初始化之后設置數據源,代理,完成相應的數據源方法就可以正常展示了,而且其數據源 代理方法相對來說也很少,肯花心思去思考 記憶,很快就能掌握這個控件。
一、UIPickerView 簡介
UIPickerView 是一種使用旋轉輪或一個槽機映像來顯示一列或多列,可多行展示的滾動視圖。 其與UIDatePicker 展示效果極為相似, 但是其是一個相對開發者來說更為通用的滾動選擇器。 由類名就可以得出UIDatePicker 是為日期選擇而生, 而其是一個通用適合自定義視圖展示的選擇器。
UIPickerView 繼承自 UIView。 UIDatePicker繼承自Control。
兩者不同的父類也直接導致了 UIPickerView 相對 UIDatePicker 也缺少了很多 Control 自帶的特性。畢竟 Control是從 UIView 繼承出來的。
二、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 方法,直接設置背景即可。
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";
}
效果如圖:
也可以通過另外兩個代理方法來實現 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];
}
效果如圖:
到這里 UIPickerView 的介紹基本結束了,想必閱讀下來大家肯定都能動手自己寫一個適合自己業務場景使用的 UIPickerView了。
補充個實用的Tips
一、UIPickerView 默認是會顯示在中心的 視圖上下分割線, 有時候我們想去去掉這條分割線,或者改變顏色可以使用這個方法。
//清除或改變分割線的顏色等。
for(UIView *singleLine in _pickerView.subviews)
{
if (singleLine.frame.size.height < 1)
{
singleLine.backgroundColor = [UIColor clearColor];
[singleLine removeFromSuperview];
}
}
最后
本文參考了很多前輩的文章及開發中自己總結的結論,希望此篇文章對您有所幫助,如有不對的地方,希望大家能留言指出糾正。歡迎大家一起交流學習 澤西島上咖啡!!!!!