美文网首页
【iOS】UIPickerView介绍

【iOS】UIPickerView介绍

作者: 刘大帅 | 来源:发表于2015-12-25 09:22 被阅读7895次

    学习文章

    IOS学习笔记(六)inputAccessoryView,inputView

    效果

    PickerView.gif

    简单介绍

    UIPickerView并不是一个使用频率很高的控件,但指不定你的应用中就会用到.

    UIPickerView的用法符合苹果一贯的设计,跟UITableView很像,而且比UITableView简单.

    跟UITableView做一下类比,UIPickerViewDataSource用来监听数据源,UIPickerViewDelegate用来监听事件.还有一个与UIPickerView相关的协议UIPickerViewAccessibilityDelegate,这是用来实现Accessibility and VoiceOver的,参见.

    看网上的教程说UIPickerView的高度是一定的,然而我测试,高度还是由你设定的frame决定的,并没有什么固定值.

    再说一下showsSelectionIndicator这个布尔值属性,iOS7之后没有效果了,参见

    showsSelectionIndicator在iOS7后无效果.png

    最后说一下,关于定制UIPickerView 的component 的三个代理方法

    1.
    - (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component __TVOS_PROHIBITED;  
      
    2.
    - (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  
      
    3.
    - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view __TVOS_PROHIBITED;  
    

    这三个方法的优先级是递增的,也就是说,你实现了方法3,就不走方法2,你实现了方法2,就不走方法1了.

    简单介绍的源码

    
    #import "ViewController.h"
    
    
    #define SCREEN_WIDTH  ([UIScreen mainScreen].bounds.size.width)
    #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
    
    // 还有一个与UIPickerView相关的协议UIPickerViewAccessibilityDelegate,这是用来实现Accessibility and VoiceOver的
    @interface ViewController ()<UIPickerViewDataSource,UIPickerViewDelegate>
    
    @property (nonatomic, strong) UIPickerView    *m_pickerView;
    @property (nonatomic, strong) NSMutableArray  *m_yearArray;
    @property (nonatomic, strong) NSMutableArray  *m_monthArray;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        
        [super viewDidLoad];
        
        self.title = @"UIPickerView简单介绍";
        
        self.m_yearArray  = [NSMutableArray arrayWithArray:@[@"2014年",@"2015年"]];
        self.m_monthArray = [NSMutableArray arrayWithArray:@[@"1月",@"2月",@"3月",@"4月",@"5月",@"6月",@"7月",@"8月",@"9月",@"10月",@"11月",@"12月"]];
    
        // 看网上的教程说UIPickerView的高度是一定的,然而我测试,高度还是由你设定的frame决定的,并没有什么固定值
        self.m_pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - 200, self.view.frame.size.width, 200)];
        [self.view addSubview:self.m_pickerView];
        
        self.m_pickerView.dataSource = self;
        self.m_pickerView.delegate   = self;
        
        // iOS7之后没有效果了,参见https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/UIKitUICatalog/UIPickerView.html
        self.m_pickerView.showsSelectionIndicator = YES;
    }
    
    #pragma mark - UIPickerViewDataSource
    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    
        return 2;
    }
    
    
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    
        if (component == 0) {
            
            return self.m_yearArray.count;
            
        } else {
        
            return self.m_monthArray.count;
        }
    }
    
    #pragma mark - UIPickerViewDelegate
    - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
    
        return self.view.frame.size.width/2.0;
    }
    - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component __TVOS_PROHIBITED {
    
        return 40;
    }
    
    
    - (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    
        if (component == 0) {
            
            return self.m_yearArray[row];
            
        } else {
        
            return self.m_monthArray[row];
        }
    }
    
    // 此方法的优先级高于上面的方法,实现了此方法,则上面的方法就不执行了,这个很好理解
    // 经过我的测试,此方法的reusingView并没什么用,可能是iOS7之后不再使用,方法说明中关于此参数是这样说的"A view object that was previously used for this row, but is now hidden and cached by the picker view.",另外参考http://stackoverflow.com/questions/20635949/reusing-view-in-uipickerview-with-ios-7
    - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view  {
        
        UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 40)];
        
        if (component == 0) {
            
            label.text = self.m_yearArray[row];
            
        } else {
            
            label.text = self.m_monthArray[row];
        }
        
        
        return label;
    }
    
    @end
    

    UIPickerView 作为 InputView

    我们知道,UITextField 和UITextView 有两个属性 inputView 和 inputAccessoryView,可以用来自定义键盘和键盘的附属视图,其实,这两个属性UIResponder就有,只不过是readonly.

    UIResponde两个属性.png
    根据此,我们可以将UIPickerView 作为 任何 UIResponder 的子类的 InputView ,不过,我们需要做一些调整.

    比如,我们准备让UIPickerView 作为 UIButton 的 InputView,我们需要实现一个 UIButton 的子类,在子类中,我们将这两个属性改为readwrite,并使这个子类可以成为第一响应者.

    // 将UIResponder的这两个readonly属性变为readwrite
    @property (nonatomic,strong,readwrite) __kindof UIView * inputView;
    @property (nonatomic,strong,readwrite) __kindof UIView * inputAccessoryView;  
      
    // 这个方法必须实现
    - (BOOL) canBecomeFirstResponder {
        
        return YES;
    }  
    

    接下来,我用两种方式来实现将 UIPickerView 作为 InputView ,一种将 UIPickerView封装到View层(用UIButton的子类作为例子),一种将UIPickerView 封装到控制器层(用UILabel的子类作为例子).

    封装到View层源码

    ButtonWithPickerView.h

    #import <UIKit/UIKit.h>
    
    // 此类用以说明如何将 PickerView 封装到 View 层
    @interface ButtonWithPickerView : UIButton
    
    @end  
    

    ButtonWithPickerView.m

    #import "ButtonWithPickerView.h"
    
    #define SCREEN_WIDTH  ([UIScreen mainScreen].bounds.size.width)
    #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
    
    @interface ButtonWithPickerView () <UIPickerViewDataSource,UIPickerViewDelegate>
    
    // 将UIResponder的这两个readonly属性变为readwrite
    @property (nonatomic,strong,readwrite) __kindof UIView * inputView;
    @property (nonatomic,strong,readwrite) __kindof UIView * inputAccessoryView;
    @property (nonatomic, strong) NSMutableArray  *m_yearArray;
    @property (nonatomic, strong) NSMutableArray  *m_monthArray;
    
    @property (nonatomic, strong) NSString * m_yearStr;
    @property (nonatomic, strong) NSString * m_monthStr;
    
    @end
    
    @implementation ButtonWithPickerView
    
    // 这个方法必须实现
    - (BOOL) canBecomeFirstResponder {
        
        return YES;
    }
    
    // inputView背景色设为透明是无效的
    - (UIPickerView *)inputView {
    
        if (_inputView == nil) {
            
            self.m_yearArray  = [NSMutableArray arrayWithArray:@[@"2014年",@"2015年"]];
            self.m_monthArray = [NSMutableArray arrayWithArray:@[@"1月",@"2月",@"3月",@"4月",@"5月",@"6月",@"7月",@"8月",@"9月",@"10月",@"11月",@"12月"]];
            
            self.m_yearStr  = self.m_yearArray[0];
            self.m_monthStr = self.m_monthArray[0];
            
            UIPickerView *pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, 200)];
            pickerView.dataSource = self;
            pickerView.delegate   = self;
            
            return pickerView;
        }
        
        return _inputView;
    }
    
    //  inputAccessoryView 是 UIView即可,不一定非得是UIToolbar
    - (UIToolbar*)inputAccessoryView {
    
        if (_inputAccessoryView == nil) {
            
            UIToolbar * toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 44)];
            
            UIBarButtonItem * item = [[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(btnResignFirstResponder)];
            toolBar.items = @[item];
            
            return toolBar;
        }
        
        return _inputAccessoryView;
    }
    
    - (void)btnResignFirstResponder {
    
        [self resignFirstResponder];
    }
    
    #pragma mark - UIPickerViewDataSource
    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
        
        return 2;
    }
    
    
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
        
        if (component == 0) {
            
            return self.m_yearArray.count;
            
        } else {
            
            return self.m_monthArray.count;
        }
    }
    
    #pragma mark - UIPickerViewDelegate
    - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
        
        return SCREEN_WIDTH/2.0;
    }
    - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component __TVOS_PROHIBITED {
        
        return 40;
    }
    
    
    - (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
        
        if (component == 0) {
            
            return self.m_yearArray[row];
            
        } else {
            
            return self.m_monthArray[row];
        }
    }
    
    - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
        
        if (component == 0) {
            
            self.m_yearStr = self.m_yearArray[row];
            
        } else {
        
            self.m_monthStr = self.m_monthArray[row];
        }
        
        [self setTitle:[NSString stringWithFormat:@"%@  %@",self.m_yearStr,self.m_monthStr] forState:UIControlStateNormal];
    }
    
    @end
    

    SecondViewController.m

    #import "SecondViewController.h"
    #import "ButtonWithPickerView.h"
    
    #define SCREEN_WIDTH  ([UIScreen mainScreen].bounds.size.width)
    #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
    
    @interface SecondViewController ()
    
    @end
    
    @implementation SecondViewController
    
    - (void)viewDidLoad {
        
        [super viewDidLoad];
        
        self.title = @"Button调出PickerView";
    
        self.view.backgroundColor = [UIColor whiteColor];
        
        ButtonWithPickerView *btn = [ButtonWithPickerView buttonWithType:UIButtonTypeCustom];
        [self.view addSubview:btn];
        
        btn.frame           = CGRectMake(0, 0, 300, 40);
        btn.center          = self.view.center;
        btn.backgroundColor = [UIColor purpleColor];
        [btn setTitle:@"PickView" forState:UIControlStateNormal];
        [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
    }
    
    - (void)btnAction:(UIButton*)sender {
    
        [sender becomeFirstResponder];
    }
    
    @end  
    

    封装到控制器层源码

    LabelWithPickView.h

    #import <UIKit/UIKit.h>
    
    @interface LabelWithPickView : UILabel
    
    // 将UIResponder的这两个readonly属性变为readwrite
    @property (nonatomic,strong,readwrite) __kindof UIView * inputView;
    @property (nonatomic,strong,readwrite) __kindof UIView * inputAccessoryView;
    
    @end  
    

    LabelWithPickView.m

    
    #import "LabelWithPickView.h"
    
    #define SCREEN_WIDTH  ([UIScreen mainScreen].bounds.size.width)
    #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
    
    @implementation LabelWithPickView
    
    - (instancetype)initWithFrame:(CGRect)frame {
    
        if (self = [super initWithFrame:frame]) {
            
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction)];
            [self addGestureRecognizer:tap];
        }
        
        return self;
    }
    
    - (void)tapAction {
    
        [self becomeFirstResponder];
    }
    
    // 这个方法必须实现
    - (BOOL) canBecomeFirstResponder {
        
        return YES;
    }
    
    @end  
    

    ThirdViewController.m

    #import "ThirdViewController.h"
    #import "LabelWithPickView.h"
    
    #define SCREEN_WIDTH  ([UIScreen mainScreen].bounds.size.width)
    #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
    
    @interface ThirdViewController ()<UIPickerViewDataSource,UIPickerViewDelegate>
    
    @property (nonatomic, strong) NSMutableArray    *m_yearArray;
    @property (nonatomic, strong) NSMutableArray    *m_monthArray;
    @property (nonatomic, strong) LabelWithPickView *m_label;
    
    @end
    
    @implementation ThirdViewController
    
    - (void)viewDidLoad {
        
        [super viewDidLoad];
        
        self.title = @"Label调出PickerView";
        
        self.view.backgroundColor = [UIColor orangeColor];
        
        self.m_yearArray  = [NSMutableArray arrayWithArray:@[@"2014年",@"2015年"]];
        self.m_monthArray = [NSMutableArray arrayWithArray:@[@"1月",@"2月",@"3月",@"4月",@"5月",@"6月",@"7月",@"8月",@"9月",@"10月",@"11月",@"12月"]];
    
        UIPickerView *pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 200)];
        pickerView.dataSource = self;
        pickerView.delegate   = self;
        pickerView.backgroundColor = [UIColor redColor];
        
        UIToolbar * toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 44)];
        
        UIBarButtonItem * item = [[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(toolBarItemAction)];
        toolBar.items = @[item];
        
        self.m_label = [[LabelWithPickView alloc]initWithFrame:CGRectMake((SCREEN_WIDTH - 300)/2.0, 100, 300, 40)];
        [self.view addSubview:self.m_label];
        
        self.m_label.userInteractionEnabled = YES;
        self.m_label.backgroundColor        = [UIColor redColor];
        self.m_label.text                   = @"Tap Me";
        self.m_label.textAlignment          = NSTextAlignmentCenter;
    
        // inputView背景色设为透明是无效的
        self.m_label.inputView              = pickerView;
        
        //  inputAccessoryView 是 UIView即可,不一定非得是UIToolbar
        self.m_label.inputAccessoryView     = toolBar;
        
        
        self.m_label.inputView.backgroundColor = [UIColor clearColor];
    }
    
    - (void)toolBarItemAction {
    
        [self.m_label resignFirstResponder];
        
        UIPickerView *pickerView = self.m_label.inputView;
        
        NSInteger year  = [pickerView selectedRowInComponent:0];
        NSInteger month = [pickerView selectedRowInComponent:1];
        
        NSString *yearStr  = self.m_yearArray[year];
        NSString *monthStr = self.m_monthArray[month];
        
        self.m_label.text = [NSString stringWithFormat:@"%@  %@",yearStr,monthStr];
    }
    
    #pragma mark - UIPickerViewDataSource
    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
        
        return 2;
    }
    
    
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
        
        if (component == 0) {
            
            return self.m_yearArray.count;
            
        } else {
            
            return self.m_monthArray.count;
        }
    }
    
    #pragma mark - UIPickerViewDelegate
    - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
        
        return self.view.frame.size.width/2.0;
    }
    - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component __TVOS_PROHIBITED {
        
        return 40;
    }
    
    
    - (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
        
        if (component == 0) {
            
            return self.m_yearArray[row];
            
        } else {
            
            return self.m_monthArray[row];
        }
    }
    
    @end  
    

    下载源码

    下载地址

    相关文章

      网友评论

          本文标题:【iOS】UIPickerView介绍

          本文链接:https://www.haomeiwen.com/subject/yanvhttx.html