Swift.地址选择器

作者: 王四猫 | 来源:发表于2018-09-04 09:37 被阅读240次
    效果图

    实现效果:

    controller弹出时:半透明背景渐变展示.地址选择器从下方弹出.

    地址选择器:以省份,城市,地区三级进行选择,数据来自本地plist文件.有12个热门城市供快速选择,选择错误可以回选.

    选择地区时进行将数据回调到上一控制器,点击页面空白区域退出controller.

    controller消失时:背景渐变消失,地址选择器向下退出.

    实现思路:

    本质上来说这是一个复杂版的日期选择器,有关弹出动画完全可以拿过来就用.

    所以主要复杂的点就是这个自定制的地址选择器.

    具体实现是写一个view其中包含tableView.给view三种type.来决定它当前的状态.然后根据type的改变来修改他的样式和数据展示,以及cell被点击时执行方法.

    实现方式:

    1.找到数据源,声明model将数据转成对象.

    2.实现地址选择器的样式,数据展示.

    3.实现我们需要的转场视图动画.

    4.将地址选择器加入一个ViewController,并修改其转场动画代理,使其使用自定制转场动画.

    5.实现地址选择器的功能,以及交互优化.


    1.找到数据源,声明model将数据转成对象.

    数据来自网络,一个plist文件.将其转换成model进行保存.

    struct EWCountryModel {
        var countryDictionary: Dictionary<String,EWProvincesModel> = [:]
        var provincesArray: [String] = []
        init(dic:Dictionary<String,Dictionary<String,Array<String>>>){
            for (key,value) in dic{
                let model = EWProvincesModel(dic: value)
                countryDictionary[key] = model
                provincesArray.append(key)
            }
        }
    }
    struct EWProvincesModel{
        var provincesDictionary: Dictionary<String,EWCityModel> = [:]
        var cityArray: [String] = []
        init(dic:Dictionary<String,Array<String>>){
            for (key,value) in dic{
                let model = EWCityModel(Arr: value)
                provincesDictionary[key] = model
                cityArray.append(key)
            }
        }
    }
    struct EWCityModel{
        var areaArray: Array<String> = []
        init(Arr:Array<String>){
            for str in Arr{
                areaArray.append(str)
            }
        }
    }
    
      /// 从area.plist获取全部地区数据
        func initLocationData(){
            let dic = NSDictionary(contentsOfFile: Bundle.main.path(forResource: "area", ofType: "plist")!) as! Dictionary<String,Any>
            locationModel = EWCountryModel(dic: dic as! Dictionary<String, Dictionary<String, Array<String>>>)
            dataArray = locationModel?.provincesArray
        }
    

    2.实现地址选择器的样式,数据展示.

        func buildTitleScrollView() {
            if titleSV != nil {
                titleSV.removeFromSuperview()
            }
            buttonArr = []
            titleSV = UIScrollView(frame: CGRect(x: 0, y: 72, width: ScreenInfo.Width, height: 44))
            self.underLine = UIView(frame: CGRect(x: 0, y: 40, width: 30, height: 2))
            self.underLine.backgroundColor = UIColor.x4FB0FF
            for i in 0..<3{
                let button = UIButton(frame: CGRect(x: 24 + CGFloat(i) * (ScreenInfo.Width - 47) / 3, y: 0, width: ScreenInfo.Width / 3, height: 44))
                button.tag = Int(i)
                if i == 1 {
                    button.isSelected = true
                    underLine.center.x = button.center.x
                }
                button.setTitle("请选择", for: .normal)
                button.setTitleColor(UIColor.x333333, for: .normal)
                button.setTitleColor(UIColor.x4FB0FF, for: .selected)
                button.titleLabel?.font = UIFont.systemFont(ofSize: 12)
                button.titleLabel?.adjustsFontSizeToFitWidth = true
                button.addTarget(self, action: #selector(onClickTitlebutton(sender:)), for: .touchUpInside)
                buttonArr.append(button)
                titleSV.addSubview(button)
            }
            titleSV.showsVerticalScrollIndicator = false
            titleSV.addSubview(self.underLine)
            titleSV.contentSize = CGSize(width: ScreenInfo.Width, height: 44)
            titleSV.isHidden = true
            self.addSubview(titleSV)
        }
        func drawTableView(){
            self.addSubview(tableView)
            tableView.delegate = self
            tableView.dataSource = self
            tableView.separatorStyle = .none
            tableView.tableHeaderView = tableViewHeaderView
            tableView.showsVerticalScrollIndicator = false
            tableView.register(EWAddressPickViewTableViewCell.self, forCellReuseIdentifier: EWAddressPickViewTableViewCell.identifier)
            tableView.register(EWAddressPickViewFirstTableViewCell.self, forCellReuseIdentifier: EWAddressPickViewFirstTableViewCell.identifier)
        }
    

    3.实现我们需要的转场视图动画.

           //EWAddressPickerViewController的推出和取消动画
    class EWAddressPickerPresentAnimated: NSObject,UIViewControllerAnimatedTransitioning {
    
        var type: EWAddressPickerPresentAnimateType = .present
    
        init(type: EWAddressPickerPresentAnimateType) {
            self.type = type
        }
        /// 动画时间
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.3
        }
        /// 动画效果
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    
            switch type {
            case .present:
                let toVC : EWAddressViewController = transitionContext.viewController(forKey: .to) as! EWAddressViewController
                let toView = toVC.view
    
                let containerView = transitionContext.containerView
                containerView.addSubview(toView!)
    
                toVC.containV.transform = CGAffineTransform(translationX: 0, y: (toVC.containV.frame.height))
    
                UIView.animate(withDuration: 0.25, animations: {
                    /// 背景变色
                    toVC.backgroundView.alpha = 1.0
                    /// addresspicker向上推出
                    toVC.containV.transform =  CGAffineTransform(translationX: 0, y: -10)
                }) { (finished) in
                    UIView.animate(withDuration: 0.2, animations: {
                        /// transform初始化
                        toVC.containV.transform = CGAffineTransform.identity
                    }, completion: { (finished) in
                        transitionContext.completeTransition(true)
                    })
                }
            case .dismiss:
                let toVC : EWAddressViewController = transitionContext.viewController(forKey: .from) as! EWAddressViewController
    
                UIView.animate(withDuration: 0.25, animations: {
                    toVC.backgroundView.alpha = 0.0
                    /// addresspicker向下推回
                    toVC.containV.transform =  CGAffineTransform(translationX: 0, y: (toVC.containV.frame.height))
                }) { (finished) in
                    transitionContext.completeTransition(true)
                }
            }
        }
    }
    
    

    4.将地址选择器加入一个ViewController,并修改其转场动画代理,使其使用自定制转场动画.

     lazy var containV: EWAddressPickView = {
            let view = EWAddressPickView(frame: CGRect(x: 0, y: ScreenInfo.Height-550, width: ScreenInfo.Width, height: 550))
            view.backOnClickCancel = {
                self.onClickCancel()
            }
            /// 成功选择后将数据回调,并推出视图
            view.backLocationString = { (address,province,city,area) in
                if self.backLocationStringController != nil{
                    self.backLocationStringController!(address,province,city,area)
                    self.onClickCancel()
                }
            }
            return view
        }()
    
    //MARK: - 转场动画delegate
    extension EWAddressViewController:UIViewControllerTransitioningDelegate{
        /// 推入动画
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            let animated = EWAddressPickerPresentAnimated(type: .present)
            return animated
        }
        /// 推出动画
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            let animated = EWAddressPickerPresentAnimated(type: .dismiss)
            return animated
        }
    }
    

    5.实现地址选择器的功能,以及交互优化.

    主要复杂的部分,只能展示其中一部分代码,具体实现还是要在demo中看.

     private var tableViewType: EWLocationPickViewTableViewType = .provinces{
            didSet{
                switch tableViewType {
                case .provinces:
                    /// 选择省份时,有上面的热门城市view.没有滚动选择type的titleScrollView.没有已选择label.
                    self.tableView.tableHeaderView = tableViewHeaderView
                    self.tableView.frame = CGRect(x: 0, y: 42, width: ScreenInfo.Width, height: 458)
                    self.titleSV.isHidden = true
                    self.leftLabel.isHidden = true
                    /// 将所有选中数据清空
                    self.provincesModel = nil
                    self.selectedProvince = ""
                    self.selectedCity = ""
                    self.selectedArea = ""
                    self.cityModel = nil
                    // 将titleSV中所有button的title重置
                    // 并将第一个button设置为选中状态,已保证选择城市后button下的横线有滚动效果.
                    for button in buttonArr {
                        button.setTitle("请选择", for: .normal)
                        button.isSelected = false
                        if button.tag == 0{
                            button.isSelected = true
                        }
                    }
                    self.underLine.center = CGPoint(x: self.buttonArr[1].center.x, y: self.underLine.center.y)
                    self.dataArray = locationModel?.provincesArray
                    self.tableView.reloadData()
                case .city:
                    /// 选择城市时没有热门城市view,并将titleSV显示出来
                    self.tableView.tableHeaderView = UIView()
                    self.tableView.frame = CGRect(x: 0, y: 136, width: ScreenInfo.Width, height: 367)
                    self.titleSV.isHidden = false
                    self.leftLabel.isHidden = false
                    /// 将省份选择保留,将城市与地区数据清空
                    self.selectedCity = ""
                    self.selectedArea = ""
                    self.cityModel = nil
                    /// 通过修改titleSV中button的选中状态来修改它的颜色
                    for button in buttonArr {
                        button.isSelected = false
                        if button.tag != 0{
                            button.setTitle("请选择", for: .normal)
                        }
                        if button.tag == 1{
                            button.isSelected = true
                        }
                    }
    
                    /// 滚动titleSV中button下滚动的Line
                    UIView.animate(withDuration: 0.3, animations: {() -> Void in
                        self.underLine.center = CGPoint(x: self.buttonArr[1].center.x, y: self.underLine.center.y)
                    })
    
                    self.dataArray = provincesModel?.cityArray
                    self.tableView.reloadData()
                case .area:
                    /// 选择地区时没有上方热门城市View,有titleSV
                    self.tableView.tableHeaderView = UIView()
                    self.tableView.frame = CGRect(x: 0, y: 136, width: ScreenInfo.Width, height: 367)
                    self.titleSV.isHidden = false
                    self.leftLabel.isHidden = false
                    /// 通过修改titleSV中button的选中状态来修改它的颜色
                    for button in buttonArr {
                        button.isSelected = false
                        if button.tag == 2{
                            button.isSelected = true
                        }
                    }
                    /// 滚动titleSV中button下滚动的Line
                    UIView.animate(withDuration: 0.3, animations: {() -> Void in
                        self.underLine.center = CGPoint(x: self.buttonArr[2].center.x, y: self.underLine.center.y)
                    })
                    self.dataArray = cityModel?.areaArray
                    self.tableView.reloadData()
                }
            }
        }
    

    使用方法:

    将EWAddressPicker文件夹拖入项目,调用时:

    let addressPicker = EWAddressViewController()
    /*** 可使用这种init方法自定制选中颜色,不填写selectColor默认颜色为UIColor(red: 79/255, green: 176/255, blue: 255/255, alpha: 1),蓝色
    let addressPicker = EWAddressViewController(selectColor: UIColor.yellow)
    */
    // 返回选择数据,地址,省,市,区
    addressPicker.backLocationStringController = { (address,province,city,area) in
        self.label.text = address
    }
    self.present(addressPicker, animated: true, completion: nil)
    

    demo地址:AddressPicker.求star.

    OC版本:OC.地址选择器.

    有问题欢迎探讨.

    相关文章

      网友评论

      • 蓝蓝的白云:大哥,可以给我一份OC版的吗?跪求
        王四猫:@蓝蓝的白云 这两天我找时间看下吧,弄出来了再说
        蓝蓝的白云:@王四猫 加桥接bridge和swift.h文件都是次要的,主要界面还需要我做改动,对swift语法不是很熟悉
        王四猫:如果不需要什么大的修改可以直接调用Swift文件啊,接个bridging文件就可以了.

      本文标题:Swift.地址选择器

      本文链接:https://www.haomeiwen.com/subject/bbrdwftx.html