zoukankan      html  css  js  c++  java
  • 省市区三级联动 pickerView

    效果图

    概述

    关于 省市区 三级联动的 pickerView,我想大多数的 iOS 开发者应该都遇到过这样的需求。在遇到这样的需求的时候,大多数人都会觉的这个很复杂,一时无从下手。其实真的没那么复杂。在这里我们来一起看看,怎么去实现这样的 pickerView,并做一个简单的封装,使其使用的更加简单,从而也减少了 ViewController 中的代码。
    

    实现思路

    如何封装

    • 我们使用一个 View(IDAddressPickerView) 来封装 PickerView,来处理 PickerView 的 dataSource 和 delegate,将原本需要在 ViewController处理的 逻辑封装的 View 中。
    • ViewController 只需要为 IDAddressPickerView 提供 dataSource,并获取选中的 Address。而不去关心其他逻辑,不如说:联动逻辑,数据格式化。
    • IDAddressPickerView 使用委托模式来获取 ViewController 提供的数据源。

    数据如何组织

    • IDAddressPickerView 的数据源是一个数组,且需要满足一定的格式,这在一定程度上降低了其使用灵活性。

    • 目前 IDAddressPickerView 数据源的需要满足的格式如图:

    获取选中的地址

    • 选中地址的格式,目前是通过固定的 key 包装在一个 Dictionary,灵活性不高。
    • 在此没有使用委托等模式,而是通过一个属性保存当前选中的地址,让用户(IDAddressPickerView 的使用者)主动去获取选中的地址。

    期望结果

    • 目前实现的 IDAddressPickerView 的数据源缺乏灵活性,尽管我们可以与后台沟通约定数据格式,但是对于一个封装的 IDAddressPickerView 来说,显然是不妥当的。
    • 我期望实现的结果是,在为 IDAddressPickerView 提供数据源的时候,指定一个 dataFormatter,IDAddressPickerView 根据 dataFormatter 去解析数据源的数据。而不是现在的根据固定的格式解析数据。
    • 由于这些问题的存在,我将项目代码上传到 github 上,还希望有兴趣的小伙伴们多提宝贵意见。

    具体实现

    IDAddressPickerView

    • 自定义 UIView 的 子类 IDAddressPickerView

      @interface IDAddressPickerView : UIView
      @end
      
    • 添加 UIPickerView 子控件

      - (UIPickerView *)pickerView {
          if (_pickerView == nil) {
              _pickerView = [[UIPickerView alloc] init];
              _pickerView.dataSource = self;
              _pickerView.delegate = self;
          }
          return _pickerView;
      }
      
      • UIPickerView 的数据源

        - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
            return 3;
        }
        - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
            NSInteger numberOfRowsInComponent = 0;
            switch (component) {
                case 0:
                    numberOfRowsInComponent = self.addressArray.count;
                    break;
                case 1:
                {
                    NSDictionary *province = self.addressArray[self.provinceIndex];
                    numberOfRowsInComponent = [province[@"cities"] count];
                }
                    break;
                case 2:
                {
                    NSDictionary *province = self.addressArray[self.provinceIndex];
                    NSDictionary *cities = province[@"cities"][self.cityIndex];
                    numberOfRowsInComponent = [cities[@"areas"] count];
                }
                    break;
            }
            return numberOfRowsInComponent;
        }
        - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
            NSString *titleForRow = @"";
            switch (component) {
                case 0:
                    titleForRow = self.addressArray[row][@"state"];
                    break;
                case 1:
                {
                    NSDictionary *province = self.addressArray[self.provinceIndex];
                    titleForRow = province[@"cities"][row][@"city"];
                }
                    break;
                case 2:
                {
                    NSDictionary *province = self.addressArray[self.provinceIndex];
                    NSDictionary *city = province[@"cities"][self.cityIndex];
                    titleForRow = city[@"areas"][row];
                }
                    break;
            }
            return titleForRow;
        }
        
      • UIPickerView 的代理

        - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
            switch (component) {
                case 0:
                {
                    self.provinceIndex = row;
                    self.cityIndex = 0;
                    self.areaIndex = 0;
                    [pickerView reloadComponent:1];
                    [pickerView reloadComponent:2];
                    [pickerView selectRow:0 inComponent:1 animated:NO];
                    [pickerView selectRow:0 inComponent:2 animated:NO];
                    /**
                     *  更新选中的 addresss,包括:市,区
                     */
                    NSDictionary *province = self.addressArray[self.provinceIndex];
                    NSDictionary *city = province[@"cities"][self.cityIndex];
                    self.selectedAddress[ProvinceKey] = self.addressArray[row][@"state"];
                    if ([province[@"cities"] count] > 0) {
                        self.selectedAddress[CityKey] = province[@"cities"][0][@"city"];
                    } else {
                        self.selectedAddress[CityKey] = @"";
                    }
                    if ([city[@"areas"] count] > 0) {
                        self.selectedAddress[AreaKey] = city[@"areas"][0];
                    } else {
                        self.selectedAddress[AreaKey] = @"";
                    }
                }
                    break;
                case 1:
                {
                    self.cityIndex = row;
                    self.areaIndex = 0;
                    [pickerView reloadComponent:2];
                    [pickerView selectRow:0 inComponent:2 animated:NO];
                    /**
                     *  更新选中的 addresss,包括:区
                     */
                    NSDictionary *province = self.addressArray[self.provinceIndex];
                    NSDictionary *city = province[@"cities"][self.cityIndex];
                    self.selectedAddress[CityKey] = province[@"cities"][row][@"city"];
                    if ([city[@"areas"] count] > 0) {
                        self.selectedAddress[AreaKey] = city[@"areas"][0];
                    } else {
                        self.selectedAddress[AreaKey] = @"";
                    }
                }
                    break;
                case 2:
                {
                    self.areaIndex = row;
                    /**
                     *  更新选中的 addresss
                     */
                    NSDictionary *province = self.addressArray[self.provinceIndex];
                    NSDictionary *city = province[@"cities"][self.cityIndex];
                    self.selectedAddress[AreaKey] = city[@"areas"][row];
                }
                    break;
            }
        }
        
      • 关于 UIPickerView 的数据源

        • UIPickerView 的数据源 通过 IDAddressPickerViewDataSource 协议获得

          - (NSArray *)addressArray {
              if (_addressArray == nil) {
                  if ([self.dataSource respondsToSelector:@selector(addressArray)]) {
                      _addressArray = [self.dataSource addressArray];
                  } else {
                      _addressArray = [NSArray array];
                  }
              }
              return _addressArray;
          }
          

    联动效果的实现

    • 原理

      • 基本的原理是通过更新数据源的方式,来实现选中一列中的某一行时,更新后继(更深层次)的列。
    • 具体实现

      • 在此使用三个属性分别记录省市区三个层次的对应的列中选中的行,UIPickerView 通过这三个属性来获取对应的数据源。

      • 选中一列中的某一行时,需要更新当前列及其后继列所对应的选中行信息。

        /** 选中的省份 */
        @property (nonatomic, assign) NSInteger provinceIndex;
        /** 选中的城市 */
        @property (nonatomic, assign) NSInteger cityIndex;
        /** 选中的省份 */
        @property (nonatomic, assign) NSInteger areaIndex;
        
    • 更新后继的列

      • 来实现选中一列中的某一行时,更新 所有 后继列,默认选中第一行。即,选中第一列时,更新第二列和第三列;选中第二列时,更新第三列;选中第三列时。
      - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
          switch (component) {
              case 0:
              {
                  self.provinceIndex = row;
                  self.cityIndex = 0;
                  self.areaIndex = 0;
                  [pickerView reloadComponent:1];
                  [pickerView reloadComponent:2];
                  [pickerView selectRow:0 inComponent:1 animated:NO];
                  [pickerView selectRow:0 inComponent:2 animated:NO];
                  /**
                   *  更新选中的 addresss,包括:市,区
                   */
                  NSDictionary *province = self.addressArray[self.provinceIndex];
                  NSDictionary *city = province[@"cities"][self.cityIndex];
                  self.selectedAddress[ProvinceKey] = self.addressArray[row][@"state"];
                  if ([province[@"cities"] count] > 0) {
                      self.selectedAddress[CityKey] = province[@"cities"][0][@"city"];
                  } else {
                      self.selectedAddress[CityKey] = @"";
                  }
                  if ([city[@"areas"] count] > 0) {
                      self.selectedAddress[AreaKey] = city[@"areas"][0];
                  } else {
                      self.selectedAddress[AreaKey] = @"";
                  }
              }
                  break;
              case 1:
              {
                  self.cityIndex = row;
                  self.areaIndex = 0;
                  [pickerView reloadComponent:2];
                  [pickerView selectRow:0 inComponent:2 animated:NO];
                  /**
                   *  更新选中的 addresss,包括:区
                   */
                  NSDictionary *province = self.addressArray[self.provinceIndex];
                  NSDictionary *city = province[@"cities"][self.cityIndex];
                  self.selectedAddress[CityKey] = province[@"cities"][row][@"city"];
                  if ([city[@"areas"] count] > 0) {
                      self.selectedAddress[AreaKey] = city[@"areas"][0];
                  } else {
                      self.selectedAddress[AreaKey] = @"";
                  }
              }
                  break;
              case 2:
              {
                  self.areaIndex = row;
                  /**
                   *  更新选中的 addresss
                   */
                  NSDictionary *province = self.addressArray[self.provinceIndex];
                  NSDictionary *city = province[@"cities"][self.cityIndex];
                  self.selectedAddress[AreaKey] = city[@"areas"][row];
              }
                  break;
          }
      }
      

    IDAddressPickerViewDataSource 协议

    @protocol IDAddressPickerViewDataSource <NSObject>
    /**
     *  地址信息,指定格式的数组
     */
    - (NSArray *)addressArray;
    @end
    

    使用示例

    • 设置 textField 的 inputView 为 IDAddressPickerView

      _textField.inputView = self.addressPickerView;
      // getter
      - (IDAddressPickerView *)addressPickerView {
          if (_addressPickerView == nil) {
              _addressPickerView = [[IDAddressPickerView alloc] init];
              _addressPickerView.dataSource = self;
          }
          return _addressPickerView;
      }
      
    • IDAddressPickerViewDataSource 提供数据

      #pragma mark - IDAddressPickerViewDataSource
      - (NSArray *)addressArray {
          NSString *path = [[NSBundle mainBundle] pathForResource:@"address" ofType:@"plist"];
          NSArray *addressInfo = [NSArray arrayWithContentsOfFile:path];
          return addressInfo;
      }
      
    • 获取选中的地址

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
          NSLog(@"%@", self.addressPickerView.selectedAddress);
      }
      

    声明

    项目代码已经上传到 gitHub,若需要请自行获取:IDAddressPickerView

  • 相关阅读:
    InstallShield高级应用获取机机所有ORACLE服务列表
    InstallShield高级应用测试ORACL是否可连接
    [分享]Asp.net 页面加载顺序及常用页面事件规律
    InstallShield高级应用检查是否安装ORACLE或SQL Server
    InstallShield高级应用打开文件对话框
    InstallShield高级应用检测系统ServerPack版本,SP2前不支持则 abort
    InstallShield高级应用文件操作
    InstallShield高级应用系列目录
    c# 常用区别总结
    InstallShield高级应用测试Access是否可连接
  • 原文地址:https://www.cnblogs.com/theDesertIslandOutOfTheWorld/p/5683025.html
Copyright © 2011-2022 走看看