先上图,效果图如下
省市区三级联动的主要实现思路如下:
1. 新建一个集成UIView
的view
并重写其init
方法并布局,代码如下
/// lastContent为调用的时候传进来的省市区名称数组 eg:`@[@"江苏省", @"南京市", @"江宁区"]`
- (instancetype)initWithLastContent:(NSArray *)lastContent {
if ([super init]) {
self.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
if (lastContent) {
self.currentSelectProvince = lastContent.firstObject;
self.currentSelectCity = lastContent[1];
self.currentSelectArea = lastContent.lastObject;
}
[self setupView]; // 布局
[self HmGetArea]; // 解析数据
}
return self;
}
/// 设置view
- (void)setupView {
// 背景
self.bgV = [UIButton buttonWithType:(UIButtonTypeSystem)];
_bgV.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
_bgV.backgroundColor = HmColorRGBA(0, 0, 0, 0.4);
[_bgV setTitle:@"" forState:(UIControlStateNormal)];
[_bgV addTarget:self action:@selector(cancelAction:) forControlEvents:(UIControlEventTouchUpInside)];
[self addSubview:_bgV];
// 承载view
UIView *vv = [[UIView alloc] initWithFrame:CGRectMake(20, (kScreenHeight - kPickerViewHeight - 80) / 2, kScreenWidth - 40, kPickerViewHeight + 80)];
vv.layer.cornerRadius = 10;
vv.layer.masksToBounds = YES;
vv.backgroundColor = [UIColor whiteColor];
[self addSubview:vv];
// title
UILabel *lblTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, vv.hm_width, kTitleHeight)];
lblTitle.textColor = HmColorRGB(51, 51, 51);
lblTitle.backgroundColor = [UIColor clearColor];
lblTitle.text = @"选择邮寄地区";
lblTitle.textAlignment = NSTextAlignmentCenter;
lblTitle.font = [UIFont systemFontOfSize:14];
[vv addSubview:lblTitle];
// PickerView
self.pickerV = [[UIPickerView alloc] initWithFrame:CGRectMake(0, lblTitle.hm_bottom, vv.hm_width, kPickerViewHeight)];
self.pickerV.frame = CGRectMake(0, lblTitle.hm_bottom, vv.hm_width, kPickerViewHeight);
self.pickerV.delegate = self;
self.pickerV.dataSource = self;
self.pickerV.backgroundColor = [UIColor clearColor];
[vv addSubview:_pickerV];
// 分割线(横)
UILabel *lblLineH = [[UILabel alloc] initWithFrame:CGRectMake(0, _pickerV.hm_bottom, vv.hm_width, 1)];
lblLineH.backgroundColor = [UIColor groupTableViewBackgroundColor];
[vv addSubview:lblLineH];
// 取消
UIButton *btnCancel = [UIButton buttonWithType:(UIButtonTypeCustom)];
btnCancel.frame = CGRectMake(0, lblLineH.hm_bottom, vv.hm_width / 2 - 0.5, vv.hm_height - lblLineH.hm_bottom);
btnCancel.backgroundColor = [UIColor clearColor];
[btnCancel setTitle:@"取消" forState:(UIControlStateNormal)];
[btnCancel setTitleColor:HmColorRGB(102, 102, 102) forState:(UIControlStateNormal)];
[btnCancel addTarget:self action:@selector(cancelAction:) forControlEvents:(UIControlEventTouchUpInside)];
btnCancel.titleLabel.font = [UIFont systemFontOfSize:15];
[vv addSubview:btnCancel];
// 分割线(竖)
UILabel *lblLineV = [[UILabel alloc] initWithFrame:CGRectMake(btnCancel.hm_right, lblLineH.hm_bottom, 1, btnCancel.hm_height)];
lblLineV.backgroundColor = [UIColor groupTableViewBackgroundColor];
[vv addSubview:lblLineV];
// 确定
UIButton *btnSure = [UIButton buttonWithType:(UIButtonTypeCustom)];
btnSure.frame = CGRectMake(lblLineV.hm_right, lblLineH.hm_bottom, vv.hm_width - lblLineV.hm_right, btnCancel.hm_height);
btnSure.backgroundColor = [UIColor clearColor];
[btnSure setTitle:@"确定" forState:(UIControlStateNormal)];
[btnSure setTitleColor:HmColorRGB(83, 184, 255) forState:(UIControlStateNormal)];
[btnSure addTarget:self action:@selector(sureAction:) forControlEvents:(UIControlEventTouchUpInside)];
btnSure.titleLabel.font = [UIFont systemFontOfSize:15];
[vv addSubview:btnSure];
}
2. 省市区数据都是txt文件保存,那么第一步要做的就是把txt文档中的数据拉出来并转换为模型。
数据源文档中的省市区数据为如下形式的json数据
[
{
"name": "北京市",
"city": [
{
"name": "北京市",
"area": [
"东城区",
"西城区",
"崇文区",
"宣武区",
"朝阳区",
"丰台区",
"石景山区",
"海淀区",
"门头沟区",
"房山区",
"通州区",
"顺义区",
"昌平区",
"大兴区",
"平谷区",
"怀柔区",
"密云县",
"延庆县"
]
}
]
},
{
"name": "天津市",
"city": [
{
"name": "天津市",
"area": [
"和平区",
"河东区",
"河西区",
"南开区",
"河北区",
"红桥区",
"塘沽区",
"汉沽区",
"大港区",
"东丽区",
"西青区",
"津南区",
"北辰区",
"武清区",
"宝坻区",
"宁河县",
"静海县",
"蓟 县"
]
}
]
},...
建立的两个模型分别如下图
转换代码如下
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"aiai_area.txt" ofType:nil]];
NSArray *allArr = [HmAddressModel arrayOfModelsFromData:data error:nil];
self.allDataArr = [NSArray arrayWithArray:allArr];
3. 在当前页面声明三个参数分别存储当前选中的省市区,如果没有选中,则省市区分别默认为所有数据中第一个省、第一个省的第一个市、第一个省的第一个市的第一个区。同时还要声明三个数组分别存储省数组、市数组、区数组。
@property (nonatomic, strong) NSMutableArray *provinceArr; // 所有的省数组
@property (nonatomic, strong) NSMutableArray *cityArr; // 当前省下属的所有的市
@property (nonatomic, strong) NSMutableArray *areaArr; // 当前市下属的所有区
@property (nonatomic, strong) NSString *currentSelectProvince; // 当前选中的省名称
@property (nonatomic, strong) NSString *currentSelectCity; // 当前选中的市名称
@property (nonatomic, strong) NSString *currentSelectArea; // 当前选中的区名称
4. 实现方法来计算当前的省数组、市数组、区数组数据
/// 计算当前市区数组
- (void)calculationCityAreaArr {
// 省市区数组数据清空
[self.provinceArr removeAllObjects];
[self.cityArr removeAllObjects];
[self.areaArr removeAllObjects];
// 如果当前选中的省为nil,则默认设置为所有数据中的第一个省
if (!self.currentSelectProvince) {
self.currentSelectProvince = ((HmAddressModel *)self.allDataArr[0]).name;
}
for (HmAddressModel *model in self.allDataArr) {
[self.provinceArr addObject:model.name];
if ([self.currentSelectProvince isEqualToString:model.name]) {
// 如果当前选中的市为nil,则默认为当前省下属的第一个市
if (!self.currentSelectCity) {
self.currentSelectCity = ((HmAddressCityModel *)model.city[0]).name;
}
for (HmAddressCityModel *mo in model.city) {
[self.cityArr addObject:mo.name];
if ([mo.name isEqualToString:self.currentSelectCity]) {
// 如果当前选中的区为nil,则默认为当前市下属的第一个区
if (!self.currentSelectArea) {
self.currentSelectArea = mo.area.firstObject;
}
for (NSString *aa in mo.area) {
[self.areaArr addObject:aa];
}
}
}
}
}
[self.pickerV reloadAllComponents];
}
5. 有了数据,接下来就是要实现pickerView
的代理方法了,代码如下
#pragma mark -- UIPickerView Delegate
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 3; // 返回三列 省、市、区
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (component == 0) {
return self.provinceArr.count;
}else if (component == 1) {
return self.cityArr.count;
}else {
return self.areaArr.count;
}
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == 0) {
return self.provinceArr[row];
}else if (component == 1) {
return self.cityArr[row];
}else {
return self.areaArr[row];
}
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
return 30;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if (component == 0) {
// 滑动省一列,市和区的数据都要改变,就存储选中的省的数据,将市和区的选中数据置nil,然后通过calculationCityAreaArr 方法重新计算市区数组,之后将市和区的列滚动到特定的位置(在计算方法中如果市或区的数据为空,会默认为第一个的)
self.currentSelectProvince = self.provinceArr[row];
self.currentSelectCity = nil;
self.currentSelectArea = nil;
[self calculationCityAreaArr];
[pickerView selectRow:[self.cityArr indexOfObject:self.currentSelectCity] inComponent:1 animated:YES];
[pickerView selectRow:[self.areaArr indexOfObject:self.currentSelectArea] inComponent:2 animated:YES];
}else if (component == 1) {
self.currentSelectCity = self.cityArr[row];
self.currentSelectArea = nil;
[self calculationCityAreaArr];
[pickerView selectRow:[self.areaArr indexOfObject:self.currentSelectArea] inComponent:2 animated:YES];
}else {
self.currentSelectArea = self.areaArr[row];
}
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
if (!view) {
view = [[UIView alloc] init];
}
UILabel *lblTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, (kScreenWidth - 50) / 3, 30)];
lblTitle.textAlignment = NSTextAlignmentCenter;
lblTitle.font = [UIFont systemFontOfSize:15];
lblTitle.textColor = HmColorRGB(51, 51, 51);
if (component == 0) {
lblTitle.text = self.provinceArr[row];
}else if (component == 1) {
lblTitle.text = self.cityArr[row];
}else {
lblTitle.text = self.areaArr[row];
}
[view addSubview:lblTitle];
return view;
}
6. 接下来就是实现取消和确定按钮的点击时间咯,当然还有点击半透明背景的事件(背景因为有点击的功效,不想麻烦再添加手势,就直接使用button
来作为背景啦)~~~~~我很懒的
/// 取消
- (void)cancelAction:(UIButton *)btn {
[self removeFromSuperview];
}
/// 确定
- (void)sureAction:(UIButton *)btn {
// 这里使用了block来返回选中的省市区数据
if (self.confirmSelect) {
self.confirmSelect(@[self.currentSelectProvince, self.currentSelectCity, self.currentSelectArea]);
}
[self removeFromSuperview];
}
7. 还有一个展示的方法,虽然只有一句话,但也给封成方法了。
- (void)show {
[[[UIApplication sharedApplication] keyWindow] addSubview:self];
}
8. 在自定义view
的.h
文件中只有简单的三行代码
@property (nonatomic, copy) void (^confirmSelect)(NSArray *address);
- (instancetype)initWithLastContent:(NSArray *)lastContent;
- (void)show;
9. 最后就是使用咯 超级简单的啦
/// 这里传进去的self. currentProvince 等等的都是本页面的村储值
HmSelectAdView *selectV = [[HmSelectAdView alloc] initWithLastContent:self.currentProvince ? @[self.currentProvince, self.currentCity, self.currentArea] : nil];
selectV.confirmSelect = ^(NSArray *address) {
self.currentProvince = address[0];
self.currentCity = address[1];
self.currentArea = address[2];
self.lblSelectAd.text = [NSString stringWithFormat:@"%@ %@ %@", self.currentProvince, self.currentCity, self.currentArea];
};
[selectV show];
是不是超级简单的啦,最后说一句哈,不要说我的省市区数组没有初始化,我使用的是懒加载,没有粘上代码 哈哈哈哈哈
感觉有用就收藏一下吧 顺便给个免费的喜欢😁😁😁 如果需要省市区json数据可以问我要,也可以上网百度(没有时间把这些整理处理,就暂时这样咯,等什么时候整理出来了直接上传到github,然后把链接贴在这里)不懂得也可以问我哟
临时整理了一个小demo 有需要的可以下载看看
代码传送门