省市區三級聯動 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM