美文网首页
省市区选择组件实践-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