码迷,mamicode.com
首页 > 其他好文 > 详细

UIPickView 和 UIDatePicker

时间:2015-12-02 22:22:47      阅读:301      评论:0      收藏:0      [点我收藏+]

标签:

 

UIPickView 和 UIDatePicker

  • 1.UIPickView什么时候用?
    • 通常在注册模块,当用户需要选择一些东西的时候,比如说城市,往往 弹出一个PickerView给他们选择。
  • 2.UIPickView常见用法
    • 独立的,没有任何关系 =>菜单系统。
    • 相关联的,下一列和第一列有联系=>省会城市选择
    • 图文并帽,=>国旗选择。
  • 3.UIDatePicker什么时候用?
    • 当用户选择日期的时候,一般弹出一个UIDatePicker给用户选择。

01-UIPickView的基本使

  • UIPickView和TableView一样,想要展示数据也要设置数据源和代理
  • 设置数据源
    • self.pickView.dataSource = self;
  • 设置代理
    • self.pickView.delegate = self;
  • 遵守数据源,代理协议:

    @interface ViewController () <UIPickerViewDataSource,UIPickerViewDelegate>
    @property (weak, nonatomic) IBOutlet UIPickerView *pickView; 
    @end
  • 实现数据源代理方法:

    总共有多少列
    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 3; }
    
    第component列有多少 .
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return 4; }
    
    返回每一列的宽度
    - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent: (NSInteger)component{
    }
    
    返回第几列的高度
    - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
    return 50; }
    
    返回每一列的标题
    - (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
    return @"gaowei"; }
    
    返回每一列的视图UIView
    - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow: (NSInteger)row forComponent:(NSInteger)component reusingView: (nullable UIView *)view{
    UIButton *btn = [UIButton
    buttonWithType:UIButtonTypeContactAdd];
    return btn; } 

02-UIPickView简单Demo

  • import "ViewController.h"

    @interface ViewController ()<UIPickerViewDataSource,UIPickerViewDelegate> 
    //展示数据的pickView
    @property (weak, nonatomic) IBOutlet UIPickerView *pickView;
    
    数组当中有3个数组,每个数组代表一列.每一列数组的个数代表这列有多少 .
    @property(nonatomic,strong) NSArray *foodArray;
    
    //显示当前选中的食物
    @property (weak, nonatomic) IBOutlet UILabel *foodLabel;
    @end    
    
    @implementation ViewController
    //懒加载数据
    -(NSArray *)foodArray{
    if (_foodArray == nil) { 获取Plist 件的路径
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"foods.plist" ofType:nil];
    //从Plist文件当中加载数组.
    _foodArray = [NSArray arrayWithContentsOfFile:filePath];
    }
    return _foodArray;
    }
    
    - (void)viewDidLoad {
    [super viewDidLoad];
    //设置数据源
    self.pickView.dataSource = self; 设置代理
    self.pickView.delegate = self;
    }
    
    //总共有多少列
    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{ 数组当中有几个元素, 就展示多少列.每个元素代表一列,
    return self.foodArray.count; }
    //第component列有多少 .
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent: (NSInteger)component{
    取出当前所在的列.每一列都是一个数组.
    NSArray *array = self.foodArray[component]; 返回每 一组当中, 每个数组的个数, 也就是这组有多少行.
     return array.count;
    }
    
    返回每一行的标题
    - (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:
    (NSInteger)row forComponent:(NSInteger)component{
    //取出当前所在的列.每一列都是一个数组.
    NSArray *componentArray = self.foodArray[component];
    //返回数组当中每一个元素
    return componentArray[row]; 
    }
    
    //点击了哪列的哪一行  
    - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
    NSString *food = self.foodArray[component][row]; self.foodLabel.text = food;
    }

03-拦截用户输入

- 通过设置代理的方式来去监听文件框的改变

    //是否允许开始编辑
    - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{ return YES;
    }

    //开始编辑时调 ,(弹出键盘)became first responder
    - (void)textFieldDidBeginEditing:(UITextField *)textField{
    NSLog(@"弹出键盘"); 
    }

    //是否允许结束编辑.
    - (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
return YES; 
}

    //结束编辑时调 .
    - (void)textFieldDidEndEditing:(UITextField *)textField{
NSLog(@"结束编辑时调 ."); 
}

    //是否允许文字改变.
    //拦截用户输入.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
return NO; 
}

04- 定义国旗键盘

  • 运行例效果:

  • 技术分享
  •  

    • 点击国家时弹出的是一个国旗键盘.
    • 实现这种效果采取的方案是自定义一个TextField.修改它的弹出键盘为 个UIPickView.

    • 对应的模型:

    • 技术分享

    • 技术分享

      #import <UIKit/UIKit.h>
      注意:这里继承的是UITextField
      @interface FlagField : UITextField
      @end
      
      这个是自定义的pickView每一行的一个UIView. 
      #import "FlagView.h" 这个是每一行对应的模型.
      #import "FlagItem.h"
      @interface  FlagField()<UIPickerViewDataSource,UIPickerViewDelegate> 
      //保存加载的过期数组.
      @property(nonatomic,strong) NSMutableArray *flagArray;
      @end
      
      @implementation FlagField
      //加载数据 
      //数据内容对应的模型图:
      技术分享
      //数组当中保存的都是字典.我们看到字典就会它转成模型.
      所以新建了一个 FlagItem模型,FlagItem每一个属性,都对应着字典当中的key值.
      //懒加载数据
      -(NSMutableArray *)flagArray{
      if (_flagArray == nil) {
      //加载plist 件路径
      NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
      //根据路径从plist文件当中加载数组
      NSArray*dictArray =[NSMutableArrayarrayWithContentsOfFile:filePath];
      //创建数组
      _flagArray = [NSMutableArray array]; 
      //遍历数组当中的每一个元素,数组当中保存的都是字典.             for (NSDictionary *dict in dictArray) {
      //把字典转成FlagItem模型
      FlagItem *item = [FlagItem flagItemDict:dict];
      //把模型添加到数组当中
      [_flagArray addObject:item]; }
          }
      return _flagArray;
      }
      注意:这个地方做了两个初始化,目的是为了不论别人使用这个FlagField是从xib创建,还是从代码创建,都让它做初始化. 
      //从xib当中创建
      -(void)awakeFromNib{
      初始化.
      [self setUp];
      }
      //从代码创建
      - (instancetype)initWithFrame:(CGRect)frame{
      if (self = [super initWithFrame:frame]) {
      //初始化.
      [self setUp];
          }
      return self;
      }
      
      //初始化.
      - (void)setUp{
      //创建UIPickerView
      UIPickerView *pick = [[UIPickerView alloc] init]; 
      //设置代理
      pick.delegate = self; 
      //设置数据源
      pick.dataSource = self;
      // 定义键盘, 让弹出的键盘是一个UIPickerView.( 定义的键盘是不需要设置尺寸的.) 
      self.inputView = pick;
      }
      
      //数据源方法
      //总共有多少列.
      -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
      return 1; }
      
      //总共有多少行  
      -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ 看数组当中有多少个模型,就有多少行.
      return self.flagArray.count; }
      //设置每一行的高度
      -(CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{ 这个 度就是xib当中描述的View的 度
      return 90; 
      }
      返回每一行的控件.这里的控件是一个UIView.通过Xib描述的模型.
      -(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(
      //快速创建一个FlagView
      FlagView *flagV = [FlagView flagView];
      //取出当前行对应的模型
      FlagItem *item = self.flagArray[row]; 把模型赋值给FlagView的item属性,就会调 FlagItem的set 法,在set 法当中给FlagItem当中的 控件进 赋值.
      flagV.flagItem = item; 返回FlagView视图. return flagV;
      }
    • 执行过程:
    • 技术分享

      05-KVC模型改进- 定义国旗键盘

  • 在给 FlagView成员属性赋值的时候,发现在这里还得要自己去写UIImage, 手动去创建UIImage ,一般在模型当中保存的就是控件最想要的东 .那个这个地方,我们最好在模型当中提供的是一张图片.

    -(void)setFlagItem:(FlagItem *)flagItem{ _flagItem = flagItem;
    //给属性赋值.
    //设置名称
    self.name.text = flagItem.name;
    //设置图片.
    self.imageV.image = [UIImage imageNamed:flagItem.icon];
    }
  • 所以在这里进行改进模型,让模型当中存放的就是用户最想要的东西. 改过之后会导致一个问题,在KVC给属性赋值的时候,字典当中给的是一个字符串.这样在转的过程当中就会造成类型不匹配,发生错误.

  • 这个时候我们就要看下KVC的底层实现原理,看过后,然后通过它的特性,去避免这个问题

  • 这个是最初的字典转模型的方法.

    +(instancetype)flagItemDict:(NSDictionary *)dict{
    //创建对象
    FlagItem *item = [[self alloc] init]; 
    //通过KVC给对象的属性赋值.
    [item setValuesForKeysWithDictionary:dict]; 
    //返回 个赋值好属性的对象.
    return item;
    }
    
    //通过KVC,调用对象的[item setValuesForKeysWithDictionary:dict] 
    setValuesForKeysWithDictionary:底层实现
    //遍历字典当中的所有Key和Value值.给对应的key,value赋值       [dictenumerateKeysAndObjectsUsingBlock:^(id _Nonnullkey,id _Nonnull obj, BOOL * _Nonnull stop) {
    //给对应的key,value赋值
    [item setValue:obj forKeyPath:key];
    }];
  • setValue:forKeyPath:的底层实现:

    1.它会调用这个属性的set方法. 
    2.如果没有set方法,它会去判断有没有跟key值同名的成员属性.如果有,就直接赋值,icon = obj. 
    3.如果没有,那么它还会去判断有没有跟key值名相同带有下划线的成员属性,如果有,就直接赋值,_icon = obj.
    4.如果都没有, 就直接报错.找不到对应的成员属性.
    利用KVC会调用属性的set方法. 当给icon属性赋值时,把传进来的字符串当作图片的名称,创建一个图片,再给图片进行赋值.
    这个的参数,类型是可以改的
    - (void)setIcon:(NSString *)icon{
        UIImage *image = [UIImage imageNamed:icon];
        _icon = image; 
        }
    
    那在View当中就可以直接赋值图片了. 
    -(void)setFlagItem:(FlagItem *)flagItem{
        _flagItem = flagItem; 给属性赋值.
    //设置名称
    self.name.text = flagItem.name; 设置图 .
    //self.imageV.image = [UIImage imageNamed:flagItem.icon]; 模型当中保存的应该是最想的东西,所以在模型当中保存的应该是图片.
    self.imageV.image = flagItem.icon;
    }

    06-自定义生日键盘

    运行示例效果:

  • 技术分享
  • 点击生日时弹出的是一个日期键盘. 实现这种效果采取的方案是自定义一个TextField.修改它的弹出键盘为一个 UIDatePicker.

    #import <UIKit/UIKit.h> 注意:这 继承的是UITextField
    @interface BirthDayField : UITextField
    @end
    
    #import "BirthDayField.h"
    @implementation BirthDayField 
    注意:这个地方做了两个初始化,目的是为了不论别人使用这个FlagField是从xib创建,还是从代码创建,都让它做初始化.
    //从xib当中创建 
    -(void)awakeFromNib{
    //初始化.
    [self setUp];
    }
    
    //从代码创建
    - (instancetype)initWithFrame:(CGRect)frame{
        if (self = [super initWithFrame:frame]) {
        //初始化.
        [self setUp];
         }
        return self;
    }
    
    //初始化方法
    - (void)setUp{
    //创建UIDatePicker(日期键盘)
    UIDatePicker *pick = [[UIDatePicker alloc] init]; 
    pick.datePickerMode = UIDatePickerModeDate; ISO639语 编码(中国zh -zhongwen)
    NSLocale *local = [NSLocale localeWithLocaleIdentifier:@"zh"];
    pick.locale = local;
    //UIDatePicker没有代理方法
        监听UIDatePicker的值改变.
    [pick addTarget:self action:@selector(dateChange:)forControlEvents:UIControlEventValueChanged];
    // 定义键盘, 让弹出的键盘是 个UIPickerView.( 定义的键盘是不需要设置尺寸的.)
    self.inputView = pick;
    }
    
    //当日期改变时调 
    - (void)dateChange:(UIDatePicker *)datePick{
    //把日期转成字符串.
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; 
    //设置日期格式
    fmt.dateFormat = @"yyyy-MM-dd";
    //格式化日期.
    NSString *dateString = [fmt stringFromDate:datePick.date];
    //给日期文本框赋值.
    self.text = dateString;
    }
    @end

    07- 定义城市键盘

    #import <UIKit/UIKit.h> 
    注意:这里继承的是UITextField
    @interface CityTextField : UITextField
    @end
    
    #import "CityTextField.h"
    省模型
    #import "ProvincesItem.h"
    @interface CityTextField()<UIPickerViewDataSource,UIPickerViewDelegate>
    //省份数组.装的都是ProvincesItem模型. 
    @property(nonatomic,strong) NSMutableArray *provincesDataArray;
    //记录当前选中的是哪一个省份的角标.
    @property(nonatomic, assign) NSInteger curProIndex;
    @end
  • 加载数据

  • 数据内容对应的模型图:

  • 技术分享
  • 数组当中保存的都是字典.我们看到字典就会它转成模型.

  • 所以新建了一个 ProvincesItem模型,ProvincesItem每一个属性,都对应着字典当中的key值.

    懒加载省份数据
    -(NSMutableArray *)provincesDataArray{
    if (_provincesDataArray == nil) { 
    //创建数组
    _provincesDataArray = [NSMutableArray array]; 
    //获取plist文件的路径
    NSString*filePath= [[NSBundlemainBundle]pathForResource:@"provinces.plist" ofType:nil]; 
    //从plist 文件当中加载数据
    NSArray *tempArray = [NSArray arrayWithContentsOfFile:filePath]; 
    //遍历数组当中的每一个元素.每个元素都是一个字典,把字典转成模型
    for (NSDictionary *dict in tempArray) {
    //创建省份模型对象.快速的将字典转成模型
    ProvincesItem*item= [ProvincesItemprovincesItemWithDict:dict]; 把转好的对象保存到数组当中.
    [_provincesDataArray addObject:item]; }
        }
    return _provincesDataArray;
    }
    
    -(void)awakeFromNib{ 
    //初始化.
    [self setUp];
    }
    
    - (instancetype)initWithFrame:(CGRect)frame{
        if (self = [super initWithFrame:frame]) {
    //初始化.
    [self setUp];
        }
    return self;
    }
    //初始化方法
    - (void)setUp{
    //创建UIPickerView
    UIPickerView *pickV = [[UIPickerView alloc] init]; 
    //设置代理
    pickV.delegate = self; 
    //设置数据源.
    pickV.dataSource = self; 
    //设置弹出键盘是一个UIPickerView 
    self.inputView = pickV;
    }
    
    实现代理协议方法
    //总共有多少组
    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    //总共有两组
    return 2; 
    }
    //每组有多少行 
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    //第一组展有多少行,由省份数组决定, 数组当中有多少个省份模型,第一组就有多少行. 
    if (component == 0) {
        return self.provincesDataArray.count;
        }else{
    //第二组展有多少行,由当前选中的省份决定,当前选中哪个省份.哪个省份下的城市数组决定. 所以要先获取当前先中的是哪个省.
    ProvincesItem *proItem = self.provincesDataArray[self.curProIndex];
    //返回当前省份下城市的个数
        return proItem.cities.count;
        }
    }
    
    返回每一行展示的标题
    - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
    如果是第0列,就是省份,返回省份的名称 if (component == 0) {
    ProvincesItem *proItem = self.provincesDataArray[row];
    return proItem.name;
    }else{
    先取出选中的省份.
    ProvincesItem *proItem = self.provincesDataArray[self.curProIndex];
    //返回选中省份下,每个城市. 
    return proItem.cities[row];
        } 
    }
    
    选中某一列的某一行时会调用这个方法
    - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)rowinComponent:(NSInteger)component{ 
    //当是第0列的时候.(省份列)
    if(component == 0){ 记录当前选中的省份的角标
    self.curProIndex = row; 
    //刷新列表.
    [pickerView reloadAllComponents]; 
    //让第1列的第0行成为选中状态.(让城市列每次刷新时,都选中第 0个)
    [pickerView selectRow:0 inComponent:1 animated:YES];
    }
    
    //把选中的省份,城市显示到文本框当中. 
    //获取选中的省.
    ProvincesItem *proItem = self.provincesDataArray[self.curProIndex]; 
    //获取省份的名称
    NSString *province = proItem.name;
    //获取第一列选中的行号.(城市列)
    NSInteger seleRow = [pickerView selectedRowInComponent:1];
    //获取当前选中省下选中的城市名称.
    NSString *city = proItem.cities[seleRow];
    //显示数据
    self.text = [NSString stringWithFormat:@"%@ %@",province,city];
    }

    08- 定义城市键盘(初始化 字处理)

  • 当光标点击文本框的时候,让文本框的文字为第一个自定义键盘选中的 字. 所以监听文本框架的开始编辑的代理 开始编辑时调用,(弹出键盘)became first responder

    - (void)textFieldDidBeginEditing:(UITextField *)textField{
    }
  • 在这个代理方法当中做事情.每一个文本框当中都提供一个初始化的方法.当在控制器当中调用这个初始化的方法.方法的目的就是让开始编辑选中第一列的第一行.

    - (void)initWithText;
    国旗键盘初始化方法实现: 
    - (void)initWithText{
    [self pickerView:nil didSelectRow:0 inComponent:0]; }
    //日期键盘初始化方法实现: 
    - (void)initWithText{
    [self dateChange:self.pick]; }
    //城市键盘初始化方法实现: 
    - (void)initWithText{
    [self pickerView:self.pick didSelectRow:0 inComponent:0];
    }

    把方法的参数改成国旗,或者日期,或者城市的类型,改的一个目的是让它能够找到 initWithText 法. 传进来的参数真实类型是什么类型,它就会去调勇敢它真实类型的方法. 如果传进来的键盘类型为日期键盘,它就会调 期的初始化方法.如果真实类型为城市,它就会去调用城市的初始化方法.

    - (void)textFieldDidBeginEditing:( FlagField *)textField{ 会去找它真实类型的方法
    [textField initWithText];
    }

    还有一个方法就是在UITextFiled的分类中声明 initWithText 方法,那么在方法 - (void)textFieldDidBeginEditing:( UITextField *)textField{ [textField initWithText]; }

  • 技术分享
  • 补充点:类的加载过程
  • 技术分享
  • 1)在加载类的load方法时,系统会先加载控制器,然后是子类,再是分类,最后才是父类
  • 2)在加载类的initialize方法时,系统会先加载控制器,然后是父类,再是分类,最后是子类.

UIPickView 和 UIDatePicker

标签:

原文地址:http://www.cnblogs.com/zhoudaquan/p/5014078.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!