天天看點

iOS-UIPickerView 詳解總結

寫在文前

由于最近開發中經常碰到類似日期選擇器相關業務使用場景,雖然這個系統控件相對來說非常簡單,有點兒類似UITableView的感覺,初始化之後設定資料源,代理,完成相應的資料源方法就可以正常展示了,而且其資料源 代理方法相對來說也很少,肯花心思去思考 記憶,很快就能掌握這個控件。

一、UIPickerView 簡介

UIPickerView 是一種使用旋轉輪或一個槽機映像來顯示一列或多列,可多行展示的滾動視圖。 其與UIDatePicker 展示效果極為相似, 但是其是一個相對開發者來說更為通用的滾動選擇器。 由類名就可以得出UIDatePicker 是為日期選擇而生, 而其是一個通用适合自定義視圖展示的選擇器。

UIPickerView 繼承自 UIView。 UIDatePicker繼承自Control。

兩者不同的父類也直接導緻了 UIPickerView 相對 UIDatePicker 也缺少了很多 Control 自帶的特性。畢竟 Control是從 UIView 繼承出來的。

iOS-UIPickerView 詳解總結

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 方法,直接設定背景即可。

iOS-UIPickerView 詳解總結

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";
}
           

效果如圖:

iOS-UIPickerView 詳解總結

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];
}
           
iOS-UIPickerView 詳解總結

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];
        }
    }
           

最後

本文參考了很多前輩的文章及開發中自己總結的結論,希望此篇文章對您有所幫助,如有不對的地方,希望大家能留言指出糾正。歡迎大家一起交流學習 澤西島上咖啡 !!!!!

學習的路上, 與君共勉!!!

繼續閱讀