Swift - 省市县三级联动功能的实现(使用UIPickerView选择框)
作者:hangge | 2016-06-30 11:10
(本文代码已升级至Swift4)
1,三级联动效果图
(1)切换省时,后面市县两级数据会动态改变。
(2)切换市时,后面的县级数据会动态改变。
(3)点击“获取信息”,取得最终选择的省、市、县索引及其名字,并打印出来。

2,地址信息文件
首先我们要准备一个包含所有地址信息的plist数据文件(address.plist),省市县的数据是一级级嵌套的树形结构,如下图:

部分数据节选如下(完整数据见我上传的源码包):
3,功能实现
(1)程序启动后,首先从 address.plist 中将所有的地址数据读取出来,存储到本地变量(数组形式)。
(2)pickerView 默认加载所有省,以及第一个省的所有市数据,以及第一个市的所有县数据(如果有的话)。
(3)通过 pickerView 选中项的改变方法 didSelectRow,取得选中的列和行索引。随后调用 reloadComponent() 方法,根据索引刷新显示对应的内容。
hangge_1169.zip

部分数据节选如下(完整数据见我上传的源码包):
<plist version="1.0">
<array>
<dict>
<key>cities</key>
<array>
<dict>
<key>areas</key>
<array>
<string>普陀区</string>
<string>定海区</string>
<string>嵊泗县</string>
<string>岱山县</string>
</array>
<key>city</key>
<string>舟山</string>
</dict>
<dict>
<key>areas</key>
<array>
<string>萧山区</string>
<string>余杭区</string>
<string>桐庐县</string>
<string>淳安县</string>
<string>临安市</string>
<string>滨江区</string>
<string>富阳市</string>
</array>
<key>city</key>
<string>杭州</string>
</dict>
</array>
<key>state</key>
<string>浙江</string>
</dict>
<dict>
<key>cities</key>
<array>
<dict>
<key>areas</key>
<array>
<string>连云区</string>
<string>新浦区</string>
<string>灌云县</string>
<string>东海县</string>
</array>
<key>city</key>
<string>连云港</string>
</dict>
</array>
<key>state</key>
<string>江苏</string>
</dict>
</array>
</plist>
3,功能实现
(1)程序启动后,首先从 address.plist 中将所有的地址数据读取出来,存储到本地变量(数组形式)。
(2)pickerView 默认加载所有省,以及第一个省的所有市数据,以及第一个市的所有县数据(如果有的话)。
(3)通过 pickerView 选中项的改变方法 didSelectRow,取得选中的列和行索引。随后调用 reloadComponent() 方法,根据索引刷新显示对应的内容。
import UIKit
class ViewController:UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{
//选择器
var pickerView:UIPickerView!
//所以地址数据集合
var addressArray = [[String: AnyObject]]()
//选择的省索引
var provinceIndex = 0
//选择的市索引
var cityIndex = 0
//选择的县索引
var areaIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
//初始化数据
let path = Bundle.main.path(forResource: "address", ofType:"plist")
self.addressArray = NSArray(contentsOfFile: path!) as! Array
//创建选择器
pickerView=UIPickerView()
//将dataSource设置成自己
pickerView.dataSource=self
//将delegate设置成自己
pickerView.delegate=self
self.view.addSubview(pickerView)
//建立一个按钮,触摸按钮时获得选择框被选择的索引
let button = UIButton(frame:CGRect(x:0, y:0, width:100, height:30))
button.center = self.view.center
button.backgroundColor = UIColor.blue
button.setTitle("获取信息",for:.normal)
button.addTarget(self, action:#selector(ViewController.getPickerViewValue),
for: .touchUpInside)
self.view.addSubview(button)
}
//设置选择框的列数为3列,继承于UIPickerViewDataSource协议
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 3
}
//设置选择框的行数,继承于UIPickerViewDataSource协议
func pickerView(_ pickerView: UIPickerView,
numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return self.addressArray.count
} else if component == 1 {
let province = self.addressArray[provinceIndex]
return province["cities"]!.count
} else {
let province = self.addressArray[provinceIndex]
if let city = (province["cities"] as! NSArray)[cityIndex]
as? [String: AnyObject] {
return city["areas"]!.count
} else {
return 0
}
}
}
//设置选择框各选项的内容,继承于UIPickerViewDelegate协议
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int,
forComponent component: Int) -> String? {
if component == 0 {
return self.addressArray[row]["state"] as? String
}else if component == 1 {
let province = self.addressArray[provinceIndex]
let city = (province["cities"] as! NSArray)[row]
as! [String: AnyObject]
return city["city"] as? String
}else {
let province = self.addressArray[provinceIndex]
let city = (province["cities"] as! NSArray)[cityIndex]
as! [String: AnyObject]
return (city["areas"] as! NSArray)[row] as? String
}
}
//选中项改变事件(将在滑动停止后触发)
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int,
inComponent component: Int) {
//根据列、行索引判断需要改变数据的区域
switch (component) {
case 0:
provinceIndex = row;
cityIndex = 0;
areaIndex = 0;
pickerView.reloadComponent(1);
pickerView.reloadComponent(2);
pickerView.selectRow(0, inComponent: 1, animated: false)
pickerView.selectRow(0, inComponent: 2, animated: false)
case 1:
cityIndex = row;
areaIndex = 0;
pickerView.reloadComponent(2);
pickerView.selectRow(0, inComponent: 2, animated: false)
case 2:
areaIndex = row;
default:
break;
}
}
//触摸按钮时,获得被选中的索引
@objc func getPickerViewValue(){
//获取选中的省
let p = self.addressArray[provinceIndex]
let province = p["state"]!
//获取选中的市
let c = (p["cities"] as! NSArray)[cityIndex] as! [String: AnyObject]
let city = c["city"] as! String
//获取选中的县(地区)
var area = ""
if (c["areas"] as! [String]).count > 0 {
area = (c["areas"] as! [String])[areaIndex]
}
//拼接输出消息
let message = "索引:\(provinceIndex)-\(cityIndex)-\(areaIndex)\n"
+ "值:\(province) - \(city) - \(area)"
//消息显示
let alertController = UIAlertController(title: "您选择了",
message: message, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "确定", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
源码下载:
全部评论(9)
站长,这个能直接修改字体大小吗?
站长回复:想修改字体大小可以参考我的另一篇文章:Swift - 选择框(UIPickerView)的用法
航哥,请问这个能否做到循环显示呢?谢谢!
站长回复:循环显示其实就是你把行数设置成一个特别大的值,然后让其默认选择中间行。过段时间我也会写篇相关文章。
航哥 代码升级到swift4 之后 报错误Ambiguous use of 'subscript'
let city = province["cities"]![cityIndex]as! [String:AnyObject] 可以更新一下代码吗
站长回复:代码现已更新,你可以再看下。
航哥,我在跑真机的时候出现了Ambiguous use of 'subscript'这个报错,模拟器上没问题,请问下是什么情况?
站长回复:版本升级语法有变。文章代码已更新,你可以再看下。
我的也报 Ambiguous use of 'subscript' ,let city = (province["cities"] as! NSArray)[row] as! [String: AnyObject] 可以解决
站长回复:多谢你的提醒,代码现已修正。
航哥,这里我不用plist,数据应该怎么处理显示啊
站长回复:也可以在代码中直接定义成字典,不过由于层级太多,不管阅读还是维护远不如使用plist方便:
航哥,我在使用的时候出现了Ambiguous use of 'subscript'这个报错,请问下是什么情况?
站长回复:我又测试了下代码,没发现这个问题。我也不知道你那边是什么情况了。
航哥,有写过美国城市的三级联动选择器吗?我连网上这些三级关联数据都没找到。。。
站长回复:这个我也没写过,主要是我也没有数据源。
能升级swift3吗
站长回复:代码已更新,你可以再看下。