Swift--三级联动地址选择器

最近有空都在学习Swift并且尝试在项目中应用,今天在重写老项目的时候,写到收货地址模块,需要一个三级联动地址选择器,于是就有了今天的内容,谨以此来记录我学习Swift的点点滴滴

知道你们在找啥 先来个demo试试鲜---BWAddressPickerView

遇到的问题:

  1. 获取地址数据的路径时拿到的path一直为nil,这个找了我老半天😂😂,很低级也很致命的错误,将plist文件导入工程时,没有勾选加入到工程内,小伙伴们一定要切记切记,将资源文件导入项目中的时候,一定要记得勾选加入到工程!!!
  2. 获取到资源文件的路由后,我使用JSONSerialization将其序列化转成字典,无奈一直报3840错误,将资源文件换成.json后缀也不行,到现在我也没找出原因,而且Swift的Dictionary也不支持从path获取,最后的最后,我将接收地址数据的变量类型换成了NSDictionary,使用NSDictionary的NSDictionary(contentsOfFile: filePath)方法,在这里说一句,OC大哥就是大哥,真香,如果有大佬们知道这个原因的,可以帮小弟解个惑,小弟静候各位大佬(ps:看到这篇文章的都是大佬!!!)
  3. 初始化器中的不会执行属性的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()

不积跬步无以至千里,不积小流无以成江海,每天都进步一点,那我们离顶端的差距又近了一点

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335