iOS三级联动选择器的实现代码示例

无聊ing...封装个省市区三级联动选择器的小demo吧。

上家公司的三级地区选择器的数据是一次性通过网络请求就能获取到的,但新东家这边并不是,而是先选择了省获取省的Id再去获取市,再通过得到市的Id获取区域,show code之前,先看下需要考虑的几个点:

1)怎么设置默认值,关键代码

[self.pickerView selectRow:xxx inComponent:xxx animated:YES];

2)怎么让三级之间联动 ,关键代码

代码如下:

[self pickerView:self.pickerView didSelectRow:0 inComponent:0 ];//联动轮子1  必须得本轮有数据后触发否则crash

先看下效果图

关于设置默认值,三级联动,用UIPickView的话就是有3个轮子(component),首先我们要想到,第一次向后台发起请求,我们只能获取到第0个component的数据,只有当你滚动轮子的时候才会获取到省的Id发起请求来获得该省的市的数据,也就是第1个component的数据,依此类推,滚动第1个component发起请求来获取第2个component的数据,因此,pickView的监听轮子滚动的代理起了重要作用

代码如下:

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;

我们通过接口获取第0个component的数据,这边是后台规定的使用id=0,获取到以后,默认选中第0个component的第0个row并主动调用触发pick的轮子滚动代理来联动第1个component【要在获取数据成功后再执行这部操作,因此放在数据请求成功的回调内】,代码为

 [self pickerView:self.pickerView didSelectRow:0 inComponent:0 ];

在各轮子滚动过程中,用一个中间值

_selectedRow0记录下第0个component的选中行

_selectedRow1记录下第1个component的选中行

_selectedRow2记录下第2个component的选中行,

这里需要记住,滚动某个轮子只能对它后面的轮子产生影响,所以当滚动第0个component的时候使_selectedRow1,_selectedRow2均置为0,这里注意,上面提到的

默认选中第0个component的第0个row并主动调用触发pick的轮子滚动代理来联动第1个component

要先将轮子上的数据渲染好,设置好默认值才能主动调用监听轮子滚动的代理,否则会导致崩溃,另一个防崩溃的点如下图

发现第三级没数据的时候,如果你在代码里没加【安全措施】,那也会导致崩溃,要在请求到第三级的数据后做下判断,如果个数为空,将该级对应的数据源置为nil。(其它两级的轮子最好也加判断)

最后,由于这是个封装的类,最终要得到选中的详细信息,可通过代理或block传值给controller。

又是你们最喜欢show code环节:

.h文件

#import <UIKit/UIKit.h>

//定制代理协议
@protocol ZLMAddressPickerViewDelegate <NSObject>

- (void)addressPickerViewDidSelected:(NSString *)areaName;//点击上方完成按钮的代理传回拼接好的选中地址

- (void)addressPickerViewDidClose;//点击关闭代理

@end

@interface ZLMAddressPickerView : UIView

@property (weak, nonatomic) id<ZLMAddressPickerViewDelegate> delegate;

@end

.m文件

#import "ZLMAddressPickerView.h"
#import "AFHttpUtils.h"
#import "AreaModel.h"
@interface ZLMAddressPickerView () <UIPickerViewDataSource, UIPickerViewDelegate>
@property (strong, nonatomic) UIPickerView *pickerView;
@property (strong, nonatomic) AreaModel  *provBridge;
@property (strong, nonatomic) AreaModel  *cityBridge;
@property (strong, nonatomic) AreaModel  *areaBridge;
@property (copy, nonatomic) NSArray<Area *> * provDataArr;//省数组
@property (copy, nonatomic) NSArray<Area *> * cityDataArr;//市数组
@property (copy, nonatomic) NSArray<Area *> * areaDataArr;//区数组
@end

@implementation ZLMAddressPickerView
{
  NSInteger _selectRow0;//记录第0个轮子的选择行
  NSInteger _selectRow1;
  NSInteger _selectRow2;
  NSString *_areaString;//最后要传回的详细地址拼接字符串
  Area *_proModel;//记录下选中省的数据
  Area *_cityModel;
  Area *_areaModel;

}

- (instancetype)initWithFrame:(CGRect)frame
{
  self = [super initWithFrame:frame];
  if (self) {
    [self setup];
  }
  return self;
}

- (void)setup {

  _selectRow0 = 0;
  _selectRow1 = 0;
  _selectRow2 = 0;

  self.backgroundColor  = [UIColor whiteColor];
  UIToolbar *toolbar   = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), 44)];
  toolbar.backgroundColor = [UIColor whiteColor];
  [self addSubview:toolbar];

  UIBarButtonItem *closeItem   = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(selectAddressClose)];
  UIBarButtonItem *completeItem  = [[UIBarButtonItem alloc] initWithTitle:@"完成" style:UIBarButtonItemStylePlain target:self action:@selector(selectAddressComplete)];
  UIBarButtonItem *spaceItem   = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  toolbar.items                     = @[closeItem, spaceItem, completeItem];

  self.pickerView.frame = CGRectMake(0, 44, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) - 44);

  [self addSubview:self.pickerView];

  [self downloadProv];

}

#pragma mark - http methods

/*省*/
- (void)downloadProv {

  NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary: @{@"id":@(0)} ];

  [AFHttpUtils sendPostTaskWithUrl:[NSString stringWithFormat:@"%@/app/member/area",BASE_DOMAIN_URL] paramenters:dic successHandle:^(NSURLSessionDataTask *task, id responseObject) {

     NSLog(@"PROV:%@",responseObject);

    self.provBridge = [AreaModel mj_objectWithKeyValues:responseObject];

    if (self.provBridge.error_code==0) {

      self.provDataArr=self.provBridge.data;

       [self pickerView:self.pickerView didSelectRow:0 inComponent:0 ];//联动轮子1 必须得本轮有数据后才能触发didselect

      [self.pickerView reloadAllComponents];

    }
  } errorHandle:^(NSError *error) {

  }];

}
/*市*/
- (void)downloadCityWithId:(NSInteger)provId {

  NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary: @{@"id":@(provId)} ];

  [AFHttpUtils sendPostTaskWithUrl:[NSString stringWithFormat:@"%@/app/member/area",BASE_DOMAIN_URL] paramenters:dic successHandle:^(NSURLSessionDataTask *task, id responseObject) {

    NSLog(@"CITY:%@",responseObject);

    self.cityBridge = [AreaModel mj_objectWithKeyValues:responseObject];

    if (self.cityBridge.error_code==0) {

      self.cityDataArr=self.cityBridge.data;

      [self.pickerView reloadComponent:1];

      [self.pickerView selectRow:0 inComponent:1 animated:YES];//默认选择row0

      [self pickerView:self.pickerView didSelectRow:0 inComponent:1 ];//联动轮子2 必须得本轮有数据后才能触发didselect

      _cityModel = self.cityDataArr[_selectRow1];

      [self downloadAreaWithId:_cityModel.area_id];

    }
  } errorHandle:^(NSError *error) {

  }];

}
/*区*/
- (void)downloadAreaWithId:(NSInteger)cityId {

  NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary: @{@"id":@(cityId)} ];

  [AFHttpUtils sendPostTaskWithUrl:[NSString stringWithFormat:@"%@/app/member/area",BASE_DOMAIN_URL] paramenters:dic successHandle:^(NSURLSessionDataTask *task, id responseObject) {

    NSLog(@"AREA:%@",responseObject);

    self.areaBridge = [AreaModel mj_objectWithKeyValues:responseObject];

    if (self.areaBridge.error_code==0&&self.areaBridge.data.count>0) {

      self.areaDataArr=self.areaBridge.data;

    }else{

      self.areaDataArr=nil;

    }
    [self.pickerView reloadComponent:2];

    [self.pickerView selectRow:0 inComponent:2 animated:YES];

    [self pickerView:self.pickerView didSelectRow:0 inComponent:2 ];

  } errorHandle:^(NSError *error) {

  }];

}
#pragma mark - events response
- (void)selectAddressComplete {
  [self.delegate addressPickerViewDidSelected:_areaString];
}

- (void)selectAddressClose {
  [self.delegate addressPickerViewDidClose];
}

#pragma mark - UIPickerViewDataSource

//确定picker的轮子个数
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {

  return 3;
}

//确定picker的每个轮子的item数
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
  if (component==0) {
     return self.provDataArr.count;

  }else if(component==1){
    return self.cityDataArr.count;

  }else{
    return self.areaDataArr.count;

  }
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
  return 36;
}

//确定每个轮子的每一项显示什么内容
- (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component{

  NSDictionary * attrDic = @{NSForegroundColorAttributeName:[UIColor blackColor],
                NSFontAttributeName:[UIFont systemFontOfSize:12]};
  Area *area;
  if (component==0) {
    area = self.provDataArr[row];

  }else if(component==1){
    area = self.cityDataArr[row];

  }else{
    area = self.areaDataArr[row];

  }
   NSAttributedString * attrString = [[NSAttributedString alloc] initWithString:area.name
                         attributes:attrDic];
  return attrString;
}

//监听轮子的移动
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {

  if (component==0) {

    _selectRow0 = [pickerView selectedRowInComponent:0];

    _selectRow1 = 0;

    _selectRow2 = 0;

    _proModel  = self.provDataArr[_selectRow0];

    [self downloadCityWithId:_proModel.area_id];

  }else if(component==1){

    _selectRow1 = [pickerView  selectedRowInComponent:1];

    _selectRow2 = 0;

    _cityModel = self.cityDataArr[_selectRow1];

     [self downloadAreaWithId:_cityModel.area_id];

  }else{

    _selectRow2 = [pickerView selectedRowInComponent:2];

    if (self.areaDataArr&&self.areaDataArr.count>0) {

       _areaModel = self.areaDataArr[_selectRow2];
    }else{
      _areaModel = nil;
    }
  }
   if(_areaModel==nil){
    _areaString = [NSString stringWithFormat:@"%@ %@",_proModel.name,_cityModel.name];
   }else{
   _areaString = [NSString stringWithFormat:@"%@ %@ %@",_proModel.name,_cityModel.name,_areaModel.name];
   }
}

#pragma mark - getters and setters
- (UIPickerView *)pickerView {
  if (_pickerView == nil) {
    _pickerView = [[UIPickerView alloc] init];
    _pickerView.delegate  = self;
    _pickerView.dataSource = self;

  }
  return _pickerView;
}

@end

最后在controller中调用

(1)导入

#import "ZLMAddressPickerView.h"

(2)定义一个对象并遵守代理协议

@property (strong, nonatomic) ZLMAddressPickerView *addressPickerView;

(3)懒加载生成对象(个人习惯)

- (ZLMAddressPickerView *)addressPickerView {
  if (!_addressPickerView) {
    _addressPickerView     = [[ZLMAddressPickerView alloc] initWithFrame:CGRectMake(0, SCREEN_HEIGHT-244-64, SCREEN_WIDTH, 244)];
    _addressPickerView.delegate = self;
  }
  return _addressPickerView;
}

(4)在点击跳出三级联动选择器的地方

 [self.view addSubview:self.addressPickerView];

(5)别忘了实现代理

#pragma mark - ZLMAddressPickerViewDelegate

- (void)addressPickerViewDidSelected:(NSString *)areaName {

  self.areaLabel.text = areaName;//将传回的详细地址字符串赋值

  [self addressPickerViewDidClose];
}

- (void)addressPickerViewDidClose {

  [self.addressPickerView removeFromSuperview];
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • iOS实现自定义起始时间选择器视图

    随着界面的整体效果的各种展现, 起始时间选择器的展现也需求突出! 最近项目中发现时间选择器使用处还挺多, 数了数原型图发现有6处. 便决定自定义时间选择器视图写个 Demo, 封装好在所需控制器里直接调用! 主要功能: 调起时间选择器, 传值(起始时间/截止时间), 两者时间均要合理, 不能超过未来时间, 并且起始时间不能大于截止时间. 点击取消或空白处收起时间选择器. 如果需要可以根据自己的需求来修改界面, 效果如下: 主要步骤: 创建时间选择器Picker 且确认取消按钮实现功能逻辑 创建展

  • iOS开发之UIPickerView实现城市选择器的步骤详解

    前言 UIPickerView是一个选择器控件,它可以生成单列的选择器,也可生成多列的选择器,而且开发者完全可以自定义选择项的外观,因此用法非常灵活.UIPickerView直接继承了UIView,没有继承UIControl,因此,它不能像UIControl那样绑定事件处理方法,UIPickerView的事件处理由其委托对象完成. 本文借助于UIPickerView来实现城市选择器,第一列为省份,第二列为第一列省份对应的城市或者区,数据放在plist中,plist结构如下图所示,第一层是一个Di

  • Android开发中实现IOS风格底部选择器(支持时间 日期 自定义)

    本文Github代码链接 https://github.com/AndroidMsky/AndoirdIOSPicker 先上图吧: 这是笔者最近一个项目一直再用的一个选择器库,自己也在其中做了修改,并决定持续维护下去. 先看使用方法: 日期选择: private void showDateDialog(List<Integer> date) { DatePickerDialog.Builder builder = new DatePickerDialog.Builder(this); bui

  • iOS中使用UIDatePicker制作时间选择器的实例教程

    UIDatePicker的创建 UIDatePicker是一个可以用来选择或者设置日期的控件,不过它是像转轮一样的控件,而且是苹果专门为日历做好的控件,如下图所示: 除了UIDatePicker控件,还有一种更通用的转轮形的控件:UIPickerView,只不过UIDatePicker控件显示的就是日 历,而UIPickerView控件中显示的内容需要我们自己用代码设置.本篇文章简单介绍UIDatePicker控件,后边的文章会介绍 UIPickerView. 1.运行Xcode ,新建一个Si

  • iOS实现自定义日期选择器示例

    iOS自定义日期选择器,下面只是说明一下怎么用,具体实现请在最后下载代码看看: 效果如下: .h文件解析 选择日期选择器样式 typedef enum{ DateStyleShowYearMonthDayHourMinute = 0, DateStyleShowMonthDayHourMinute, DateStyleShowYearMonthDay, DateStyleShowMonthDay, DateStyleShowHourMinute }XHDateStyle; //日期选择器样式 @

  • iOS自定义日期、时间、城市选择器实例代码

    选择器,我想大家都不陌生,当需要用户去选择某些范围值内的一个固定值时,我们会采用选择器的方式.选择器可以直观的提示用户选择的值范围.统一信息的填写格式,同时也方便用户快速的进行选择,比如对于性别,正常情况下就只有男女两种情况,那这时候用一个选择器给用户进行选择的话,可以避免错误数据的输入,也更方便用户去填写.再比如需要获取用户的生日信息时,采用选择器的方式可以统一生日的格式,如果让用户自行输入的话,可能会出现各种各样的生日信息格式,不利于数据的存储,但是采用选择器的方式的话,用户可找到对应的日期

  • Android自定义View仿IOS圆盘时间选择器

    通过自定义view实现仿iOS实现滑动两端的点选择时间的效果 效果图 自定义的view代码 public class Ring_Slide2 extends View { private static final double RADIAN = 180 / Math.PI; private int max_progress; // 设置最大进度 private int cur_progress; //设置锚点1当前进度 private int cur_progress2; //设置锚点2进度 p

  • iOS三级联动选择器的实现代码示例

    无聊ing...封装个省市区三级联动选择器的小demo吧. 上家公司的三级地区选择器的数据是一次性通过网络请求就能获取到的,但新东家这边并不是,而是先选择了省获取省的Id再去获取市,再通过得到市的Id获取区域,show code之前,先看下需要考虑的几个点: 1)怎么设置默认值,关键代码 [self.pickerView selectRow:xxx inComponent:xxx animated:YES]; 2)怎么让三级之间联动 ,关键代码 复制代码 代码如下: [self pickerVi

  • 微信小程序三级联动选择器使用方法

    本文实例为大家分享了微信小程序三级联动选择器的具体代码,供大家参考,具体内容如下 效果图 实现原理 利用微信小程序的picker组件,其中: 1,普通选择器:mode = selector实现一级选择实例: 2,省市区选择器:mode = region实现省市区三级联动: 3, 多列选择器:mode = multiSelector实现二级和三级联动的10以内数字的乘法. WXML <view class="tui-picker-content"> <view clas

  • 原生js三级联动的简单实现代码

    实例如下: <!DOCTYPE html> <head> <title> 三级联动 </title> <meta charset="utf-8"> </head> <body> <script> window.onload = function() { console.log(city) var oDiv = document.getElementById("div");

  • django中上传图片分页三级联动效果的实现代码

    Django1.8.2中文文档:Django1.8.2中文文档 上传图片配置上传文件保存目录 1)新建上传文件保存目录. 2)配置上传文件保存目录. 后台管理页面上传图片 1)设计模型类. 2)迁移生成表格. 3) 注册模型类. 后台管理页面上传图片实例 1.在static下面创建media文件夹(再在media文件夹里面新建booktest文件夹). 2.设置静态文件保存目录 # 设置上传文件的保存目录 MEDIA_ROOT = os.path.join(BASE_DIR, 'static/m

  • 三级联动省市ajax的代码

    目录 创建数据库 首先创建 City 和 Province 类 给getter setter tostring 以及 构造方法 Province类 City类 连接数据库 创建 接口 ProvinceInfoDao 实例化对象ProvinceInfoDaoImpl 以及CityInfoDao接口 CityInfoDaoImpl实例化对象 FindProvinceServlet FindCityPidServlet 最后是jsp页面 总结 我只写到了市剩下的区可以复制粘贴的很简单 所需要的jar包

  • ajax三级联动实现代码

    本文实例为大家分享了ajax三级联动展示的具体代码,供大家参考,具体内容如下 1. test.php <script src="../jquery-1.11.2.min.js"></script> <script src="sanji.js"></script> <title>无标题文档</title> </head> <body> <h1>三级联动</

  • JavaScript省市区三级联动菜单效果

    本文实例为大家分享了三级联动省市区js完整代码,供大家参考,具体内容如下 <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> <script type="text/javascript"> var c

  • ajax实现省市三级联动效果

    本文实例为大家分享了ajax实现三级联动效果的具体代码,供大家参考,具体内容如下 1.html代码 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style type="text/css"> .wrap { background-color: beige; width: 400px

  • 微信小程序实现联动选择器

    本文实例为大家分享了微信小程序实现联动选择器的具体代码,供大家参考,具体内容如下 picker 从底部弹起的滚动选择器,现支持五种选择器,通过mode来区分,分别是普通选择器,多列选择器,时间选择器,日期选择器,省市区选择器,默认是普通选择器. 先来看看效果图: 1.普通选择器 mode = selector(默认的) <view class='picker'>普通选择器</view> <!-- value: value值表示选择了让的第几个,index===下标 从0开始

  • layui自定义插件citySelect实现省市区三级联动选择

    本文实例为大家分享了layui实现省市区三级联动选择的具体代码,供大家参考,具体内容如下 省市区三级菜单联动插件 /** * @ name : citySelect 省市区三级选择模块 * @ Author: aggerChen * @ version: 1.0 */ layui.define(['layer','form','element','laytpl'], function(exports){ var $ = layui.$; var form = layui.form; var la

随机推荐