美文网首页
省市区选择组件实践-2020-04-21

省市区选择组件实践-2020-04-21

作者: 勇往直前888 | 来源:发表于2020-04-24 18:00 被阅读0次

    简介

    一般省市区的选择组件采用UIPickerView来实现,比如这篇文章就很好iOS-省市区选择的实现
    。这次为了和Android保持风格一致,采用自定义视图的方式。大概的样子如下:

    企业微信截图_400eb34d-1d9d-43de-ad6b-e2aebee15993.png

    数据源

    • 省市区数据一般放在后台,可以一个接口给(省市区数据一次给全);也可以分三个接口给(先给省的,选了省之后再根据选择情况给市,然后再根据市的情况给区)。

    • 考虑到省市区数据一般比较稳定,数据虽然有点多,但也不是特别大,所以可以考虑采用文件的方式打包进客户端。

    • 一般iOS访问比较方便的格式是plist,不过考虑到和Android的通用性,还是采用json的方式。网上也有这样的数据提供,比如这个

    • 从网上下载数据之后,通过网络工具,检查一下是否json格式是否正确。比如这个

    企业微信截图_d6ad669e-5ed0-4e6b-967f-f87135b71fb9.png

    使用json格式检查工具的另外一个好处是可以将文本内容格式化,查看起来更方便

    • 将内容复制过来,以.json格式存为本地文件,并且加入工程当中。
    企业微信截图_ff99b704-9ae1-468f-8fdc-31a38b936975.png
    • 读取本地json文件内容:基本思路是将文件内容读取成NSData格式,然后通过json转换方法,转换为字典和数组的嵌套结构。
      iOS:本地json文件读取、存储
    // 读取本地JSON文件
    - (NSArray *)readLocalFileWithName:(NSDictionary *)name {
        // 获取文件路径;注意,如果在framework中,这里就不一定是mainBundle了
        NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"json"];
        // 将文件数据化
        NSData *data = [[NSData alloc] initWithContentsOfFile:path];
        // 对数据进行JSON格式化并返回字典形式
        return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    }
    

    数据Model化

    • json文件读取之后,是字典和数组的嵌套结构,查看和使用都不是很方便。所以考虑Model话,这样看起来更有意义。原始的json数据如下图所示,嵌套较多。
    企业微信截图_b0dbeb1b-9764-46eb-97cb-5375ecb33014.png
    • 省市区,一般需要两个字段,一个是名称,比如我们常见的“北京”;另外一个是对应的统一编码,比如“110000”。

    • 第三级区,就只有名字和编码两个字段

    @interface ZXSDistrictModel : NSObject
    
    // 名称
    @property (copy, nonatomic) NSString *name;
    
    // 编码
    @property (copy, nonatomic) NSString *code;
    
    @end
    
    • 第二级市,除了名字和编码两个字段外,还有一个所属区的数组。
    @interface ZXSCityModel : NSObject
    
    // 名称
    @property (copy, nonatomic) NSString *name;
    
    // 编码
    @property (copy, nonatomic) NSString *code;
    
    // 所属的区
    @property (copy, nonatomic) NSArray<ZXSDistrictModel *> *districtArray;
    
    @end
    
    • 第一级省,除了名字和编码两个字段外,还有一个所属市的数组。
    @interface ZXSProvinceModel : NSObject
    
    // 名称
    @property (copy, nonatomic) NSString *name;
    
    // 编码
    @property (copy, nonatomic) NSString *code;
    
    // 所属的市
    @property (copy, nonatomic) NSArray<ZXSCityModel *> *cityArray;
    
    @end
    
    • 将字典数组嵌套结构转换为Model数组之后,结构就简单清晰多了,可读性也大大提高
    企业微信截图_212c5123-ec46-48e8-b3aa-8c99f4026048.png

    界面

    • 界面可以采用故事版,简单直接
    企业微信截图_078e9b94-512a-4372-9b4e-ad832d2fa27a.png
    • 省市区都包括namecode两个信息,对于表格来说,还有一个信息就是是否选中,所以用ZXSAreaCellModel对象来记录表格所需要的信息。
    @interface ZXSAreaCellModel : NSObject
    
    // 省市区的名称
    @property (copy, nonatomic) NSString *name;
    
    // 省市区的编码
    @property (copy, nonatomic, nullable) NSString *code;
    
    // 是否选中
    @property (assign, nonatomic) BOOL isSelect;
    
    @end
    
    • 写三个生成函数,分别通过省、市、区对象获得:
    // 通过省对象创建实例
    + (instancetype)instanceWithProvinceModel:(ZXSProvinceModel *)province {
        ZXSAreaCellModel *cellModel = [[ZXSAreaCellModel alloc] init];
        cellModel.name = province.name;
        cellModel.code = province.code;
        cellModel.isSelect = NO;
        return cellModel;
    }
    
    // 通过市对象创建实例
    + (instancetype)instanceWithCityModel:(ZXSCityModel *)city {
        ZXSAreaCellModel *cellModel = [[ZXSAreaCellModel alloc] init];
        cellModel.name = city.name;
        cellModel.code = city.code;
        cellModel.isSelect = NO;
        return cellModel;
    }
    
    // 通过区对象创建实例
    + (instancetype)instanceWithDistrictModel:(ZXSDistrictModel *)district {
        ZXSAreaCellModel *cellModel = [[ZXSAreaCellModel alloc] init];
        cellModel.name = district.name;
        cellModel.code = district.code;
        cellModel.isSelect = NO;
        return cellModel;
    }
    
    • 创建一个继承自UITableViewCell的对象来表示表格中的一项。
    @interface ZXSAreaCell : UITableViewCell
    
    // 更新cell
    - (void)updateWithCellModel:(ZXSAreaCellModel *)cellModel;
    
    @end
    

    只有两个对象,表示名称的标签,以及选中标志图片

    @interface ZXSAreaCell ()
    
    // 选项标签
    @property (weak, nonatomic) IBOutlet UILabel *itemLabel;
    
    // 选中标志
    @property (weak, nonatomic) IBOutlet UIImageView *selectImage;
    
    @end
    
    • 需要一个通过ZXSAreaCellModel对象更新cell界面显示的方法:
    // 更新cell
    - (void)updateWithCellModel:(ZXSAreaCellModel *)cellModel {
        if (cellModel) {
            self.itemLabel.text = cellModel.name;
            self.selectImage.hidden = !cellModel.isSelect;
        }
    }
    

    调用接口

    • 首先要有一个表示用户选择信息的数据接口,记录省市区namecode两个数据:
    @interface ZXSAreaModel : NSObject
    
    // 省名称
    @property (copy, nonatomic) NSString *provinceName;
    
    // 省编码
    @property (copy, nonatomic) NSString *provinceCode;
    
    // 市名称
    @property (copy, nonatomic) NSString *cityName;
    
    // 市编码
    @property (copy, nonatomic) NSString *cityCode;
    
    // 区名称
    @property (copy, nonatomic) NSString *districtName;
    
    // 区编码
    @property (copy, nonatomic) NSString *districtCode;
    
    @end
    
    • 由于我们准备以Present的方式展示省市区选择,所以需要调用者提供一个UIViewController

    • 另外,需要记住上次用户的选择,这也是一个ZXSAreaModel对象

    • 用户选择或者取消之后,可能会有后续操作,所以提供两个block,供使用者定义后续操作。

    @interface ZXSAreaSelector : NSObject
    
    // present方式显示地区选择
    + (void)launchOnContorller:(UIViewController *)vc currentArea:(nullable ZXSAreaModel *)currentArea select:(nullable void(^)(ZXSAreaModel *selectArea))selectBlock
                        cancel:(nullable void(^)(void))cancelBlock;
    
    @end
    

    控制器的属性

    数据接口所需要信息都是通过控制的外部属性提供的。

    @interface ZXSAreaSelectorViewController : UIViewController
    
    // 当前选中地区;
    @property (strong, nonatomic, nullable) ZXSAreaModel *currentArea;
    
    // 选择后的block
    @property (copy, nonatomic, nullable) void(^selectBlock)(ZXSAreaModel *selectArea);
    
    // 取消的block
    @property (copy, nonatomic, nullable) void(^cancelBlock)(void);
    
    // 所有的省数据
    @property (strong, nonatomic, nonnull) NSArray<ZXSProvinceModel *> *provinceArray;
    
    @end
    

    属性观察

    • 最方便的是使用第三方库ReactiveObjC

    • 这里内容不多,可以直接使用setter函数自定义来替代。比如:

    // 根据索引设置下划线位置和标签颜色
    - (void)setAreaIndex:(ZXSAreaIndex)areaIndex {
        _areaIndex = areaIndex;
        
        // 下划线位置
        self.maskLeftConstant.constant = areaIndex * kIndexStep;
        
        // 标签颜色
        switch (areaIndex) {
            case ZXSAreaIndexProvince:
                self.provinceLabel.textColor = kBlueColor;
                self.cityLabel.textColor = kBlackColor087;
                self.districtLabel.textColor = kBlackColor087;
                break;
            case ZXSAreaIndexCity:
                self.provinceLabel.textColor = kBlackColor087;
                self.cityLabel.textColor = kBlueColor;
                self.districtLabel.textColor = kBlackColor087;
                break;
            case ZXSAreaIndexDistrict:
                self.provinceLabel.textColor = kBlackColor087;
                self.cityLabel.textColor = kBlackColor087;
                self.districtLabel.textColor = kBlueColor;
                break;
            default:
                break;
        }
        
        // 表格数据源设置
        switch (areaIndex) {
            case ZXSAreaIndexProvince:
                self.dataSource = self.provinceDataSource;
                break;
            case ZXSAreaIndexCity:
                self.dataSource = self.cityDataSource;
                break;
            case ZXSAreaIndexDistrict:
                self.dataSource = self.districtDataSource;
                break;
            default:
                break;
        }
    }
    

    使用方式

    用起来就相对比较简单:

    @interface ZXSViewController ()
    
    @property (strong, nonatomic) ZXSAreaModel *area;
    
    @end
    
    - (IBAction)areaSelectorButtonTouched:(id)sender {
        [ZXSTool launchAreaSelectorOnContorller:self currentArea:self.area select:^(ZXSAreaModel * _Nonnull selectArea) {
            self.area = selectArea;
            NSLog(@"%@%@%@", selectArea.provinceName, selectArea.cityName, selectArea.districtName);
            NSLog(@"%@%@%@", selectArea.provinceCode, selectArea.cityCode, selectArea.districtCode);
        } cancel:nil];
    }
    

    Demo地址

    这个组件已经上传GitHub,支持CocoaPod

    ZXSTool

    相关文章

      网友评论

          本文标题:省市区选择组件实践-2020-04-21

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