最近有空都在学习Swift并且尝试在项目中应用,今天在重写老项目的时候,写到收货地址模块,需要一个三级联动地址选择器,于是就有了今天的内容,谨以此来记录我学习Swift的点点滴滴
知道你们在找啥 先来个demo试试鲜---BWAddressPickerView
遇到的问题:
- 获取地址数据的路径时拿到的path一直为nil,这个找了我老半天😂😂,很低级也很致命的错误,将plist文件导入工程时,没有勾选加入到工程内,小伙伴们一定要切记切记,将资源文件导入项目中的时候,一定要记得勾选加入到工程!!!
- 获取到资源文件的路由后,我使用JSONSerialization将其序列化转成字典,无奈一直报3840错误,将资源文件换成.json后缀也不行,到现在我也没找出原因,而且Swift的Dictionary也不支持从path获取,最后的最后,我将接收地址数据的变量类型换成了NSDictionary,使用NSDictionary的NSDictionary(contentsOfFile: filePath)方法,在这里说一句,OC大哥就是大哥,真香,如果有大佬们知道这个原因的,可以帮小弟解个惑,小弟静候各位大佬(ps:看到这篇文章的都是大佬!!!)
- 初始化器中的不会执行属性的didSet和willSet方法,所以我在初始化器中手动的调用了设置默认选中地址的操作
特点
说起这个有些害臊,哈哈哈 其实没啥特点 拿出来凑个数吧 哈哈哈
支持三级省市区联动 二级省市联动 一级省联动 支持默认选中
默默的上个图 大佬们应该没啥意见 哈哈哈
二级联动
三级联动
代码部分
- 初始化
/// 便利初始化器
/// - Parameter block: 回调闭包
convenience init(selectedBlock block: BWAddressPickerResultBlock?) {
self.init(autoSeleceted: false, selectedBlock: block)
}
/// 便利初始化器
/// - Parameters:
/// - isAutoSelected: 是否支持默认回调
/// - selectedBlock: 回调闭包
convenience init(autoSeleceted isAutoSelected: Bool = false,
selectedBlock: BWAddressPickerResultBlock?) {
self.init(autoSeleceted: isAutoSelected,mode: .area, regions: nil, selectedBlock: selectedBlock)
}
/// 便利初始化器
/// - Parameters:
/// - isAutoSelected: 是否支持默认回调
/// - mode: 地址选择器mode
/// - defaultRegions: 默认选中的地址
convenience init(autoSeleceted isAutoSelected: Bool = false,
mode: BWAddressPickerMode = .area,
defaultRegions: (String, String, String)?) {
self.init(autoSeleceted: isAutoSelected,mode: mode, regions: defaultRegions, selectedBlock: nil)
}
/// 便利初始化器
/// - Parameters:
/// - isAutoSelected: 是否支持默认回调
/// - mode: 地址选择器mode
/// - regions: 默认选中的地址
/// - selectedBlock: 回调闭包
convenience init(autoSeleceted isAutoSelected: Bool = false,
mode: BWAddressPickerMode = .area,
regions: (String, String, String)?,
selectedBlock: BWAddressPickerResultBlock?) {
self.init()
shouldAutoSelect = isAutoSelected
pickerMode = mode
self.defaultRegions = regions
addressSelectBlock = selectedBlock
//由于在初始化的时候不会执行didSet和willSet方法 此处手动调用设置默认选中值的方法
if self.defaultRegions != nil {
configDefaultData()
}
}
- 初始数据以及默认选中数据的配置
//配置数据
func configData() {
selectedProvinceModel = provinceDataSource[selectedProvinceIndex]
switch pickerMode {
case .area:
selectedCityModel = selectedProvinceModel?.child[selectedCityIndex]
selectedAreaModel = selectedCityModel?.child[selectedAreaIndex]
case .city:
selectedCityModel = selectedProvinceModel?.child[selectedCityIndex]
case .province:
selectedProvinceModel = provinceDataSource[selectedProvinceIndex]
}
}
//配置默认数据
func configDefaultData() {
//元组解包
let (province, city, area) = defaultRegions!
//默认选中的省数据以及下标
for (index, provinceModel) in provinceDataSource.enumerated() {
if provinceModel.title == province {
selectedProvinceIndex = index
break
} else {
if index == provinceDataSource.count - 1 {
selectedProvinceIndex = 0
}
}
}
selectedProvinceModel = provinceDataSource[selectedProvinceIndex]
pickerView.selectRow(selectedProvinceIndex, inComponent: 0, animated: true)
if pickerMode == .city || pickerMode == .area {
if selectedProvinceModel?.child.count ?? 0 > 0 {
for (index, cityModel) in (selectedProvinceModel?.child.enumerated())! {
if cityModel.title == city {
selectedCityIndex = index
break
} else {
if index == (selectedProvinceModel?.child.count)! - 1 {
selectedCityIndex = 0
}
}
}
selectedCityModel = selectedProvinceModel?.child[selectedCityIndex]
pickerView.selectRow(selectedCityIndex, inComponent: 1, animated: true)
}
}
if pickerMode == .area {
if selectedCityModel?.child.count ?? 0 > 0 {
for (index, areaModel) in (selectedCityModel?.child.enumerated())! {
if areaModel.title == area {
selectedAreaIndex = index
break
} else {
if index == selectedCityModel?.child.count ?? 0 - 1 {
selectedAreaIndex = 0
}
}
}
selectedAreaModel = selectedCityModel?.child[selectedAreaIndex]
pickerView.selectRow(selectedAreaIndex, inComponent: 2, animated: true)
}
}
pickerView.reloadAllComponents()
}
- UIPickerViewDataSource部分
func numberOfComponents(in pickerView: UIPickerView) -> Int {
switch pickerMode {
case .area:
return 3
case .city:
return 2
case .province:
return 1
}
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch component {
case 0:
return provinceDataSource.count
case 1:
return selectedProvinceModel?.child.count ?? 0
case 2:
return selectedCityModel?.child.count ?? 0
default:
return 0
}
}
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return BWScreenScaleWidth(width: 35)
}
- UIPickerViewDelegate部分
这里是地址选择联动的核心代码
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
//设置分割线颜色
for subView in pickerView.subviews {
if subView.isKind(of: UIView.self) && subView.height <= 1 {
subView.backgroundColor = BWHexColor("#EAEAEA")
}
}
var label = view as? UILabel
if label == nil {
label = UILabel.init()
label?.font = BWSystemRegularFont(fontSize: 14)
label?.textColor = color333333
label?.textAlignment = .center
label?.minimumScaleFactor = 0.5
label?.adjustsFontSizeToFitWidth = true
}
switch component {
case 0:
//省标题
let provinceModel = provinceDataSource[row]
label?.text = provinceModel.title
case 1:
let cityModel = selectedProvinceModel?.child[row]
label?.text = cityModel?.title
case 2:
let areaModel = selectedCityModel?.child[row]
label?.text = areaModel?.title
default:
break
}
return label!
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch component {
case 0:
selectedProvinceIndex = row
selectedProvinceModel = provinceDataSource[row]
switch pickerMode {
case .province:
selectedCityModel = nil
selectedAreaModel = nil
case .city:
selectedCityIndex = 0
//避免港澳台地区没有下级市的情况
if selectedProvinceModel?.child.count ?? 0 > 0 {
selectedCityModel = selectedProvinceModel?.child[selectedCityIndex]
pickerView.selectRow(selectedCityIndex, inComponent: 1, animated: true)
} else {
selectedCityModel = nil
}
selectedAreaModel = nil
//刷新下级市数据
pickerView.reloadComponent(1)
case .area:
//刷新下级市以及下级县数据
selectedCityIndex = 0
if selectedProvinceModel?.child.count ?? 0 > 0 {
selectedCityModel = selectedProvinceModel?.child[selectedCityIndex]
selectedAreaIndex = 0
pickerView.selectRow(selectedCityIndex, inComponent: 1, animated: true)
if selectedCityModel?.child.count ?? 0 > 0 {
selectedAreaModel = selectedCityModel?.child[selectedAreaIndex]
pickerView.selectRow(selectedAreaIndex, inComponent: 2, animated: true)
} else {
selectedAreaModel = nil;
}
} else {
selectedCityModel = nil
selectedAreaModel = nil;
}
pickerView.reloadComponent(1)
pickerView.reloadComponent(2)
}
case 1:
selectedCityIndex = row
selectedCityModel = selectedProvinceModel?.child[row]
switch pickerMode {
case .city:
selectedAreaModel = nil
case .area:
selectedAreaIndex = 0
//避免某些下级市没有更下级的县区造成的数组越界问题
if selectedCityModel?.child.count ?? 0 > 0 {
selectedAreaModel = selectedCityModel?.child[selectedAreaIndex]
pickerView.selectRow(selectedAreaIndex, inComponent: 2, animated: true)
} else {
selectedAreaModel = nil
}
pickerView.reloadComponent(2)
default:
break
}
case 2:
selectedAreaIndex = row
selectedAreaModel = selectedCityModel?.child[selectedAreaIndex]
default:
break
}
if shouldAutoSelect == true {
selectedAddress()
}
}
使用
let addressPickerView = BWAddressPickerView.init(autoSeleceted: true, mode: pickerMode, regions: ("浙江省", "湖州市", "长兴县")) { (provinceModel, cityModel, areaModel) in
print("block回调-----省:\(provinceModel.title)----市:\(cityModel?.title)----区县:\(areaModel?.title)")
}
// addressPickerView.defaultRegions = ("浙江省", "湖州市", "长兴县")
addressPickerView.delegate = self
addressPickerView.animationShow()
网友评论