swift-城市搜索选择器

代码:

import UIKit

typealias MyCityBlock = (_ str:String)->Void

class CitySelectorViewController: UIViewController {
    //城市列表
    lazy var cityTBView:UITableView = UITableView(frame: UIScreen.mainBounds, style: .plain)
    //搜索结果控制器
    lazy var searchResultVC:ResultSearchView = {
        let resultVC = ResultSearchView(frame: CGRect.init(x: 0, y: -UIScreen.mainHeight, width: UIScreen.mainWidth, height: UIScreen.mainHeight), style: .plain)
        resultVC.backgroundColor = UIColor.white
        return resultVC
    }()
    //返回按钮
    lazy var backBtn:UIButton = {
        let btn = UIButton(frame: CGRect(x: 10, y: 10, width: 24, height: 24))
        btn.setBackgroundImage(UIImage(named:"关闭"), for: .normal)
        btn.addTarget(self, action: #selector(backToHomeView), for: .touchUpInside)
        return btn
    }()
    //搜索控制器
    lazy var searchView:UIView = {
        let searchV = UIView(frame:CGRect(x: UIScreen.mainWidth*0.2, y: 7, width: UIScreen.mainWidth*0.6, height: 30))
        searchV.addSubview(self.searchBar)
        searchV.layer.masksToBounds = true
        searchV.layer.cornerRadius = 15
        searchV.backgroundColor = UIColor.white
        return searchV
    }()
    //搜索图标
    lazy var searchImgView:UIImageView = {
        let imgV = UIImageView(image: UIImage(named: "搜索"))
        imgV.contentMode = .scaleAspectFit
        return imgV
    }()
    //搜索框
    lazy var searchBar:UITextField = {
        let textF = UITextField(frame: CGRect(x: 30, y: 0, width: UIScreen.mainWidth*0.6-60, height: 30))
        textF.leftView = self.searchImgView
        textF.font = Font.large
        textF.placeholder = "输入城市名称或拼音"
        textF.textColor = Color.textH
        textF.clearButtonMode = UITextFieldViewMode.always
        textF.tintColor = Color.topNav
        textF.leftViewMode = UITextFieldViewMode.always
        textF.delegate = self
        textF.returnKeyType = UIReturnKeyType.done
        return textF
    }()
    //取消按钮
    lazy var cancleBtn:UIButton = {
        let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
        btn.setTitle("取消", for: .normal)
        btn.titleLabel?.font = Font.large
        btn.isHidden = true
        btn.addTarget(self, action: #selector(endSearching), for: .touchUpInside)
        return btn
    }()

    //获取城市数据
    lazy var cityDic:[String:[String]] = {
        let path = Bundle.main.path(forResource: "cities", ofType: "plist")
        let dic = NSDictionary(contentsOfFile: path!)
        return dic as! [String : [String]]
    }()
    //热门城市
    lazy var hotCities:[String] = {
        let path = Bundle.main.path(forResource: "hotCities", ofType: "plist")
        let array = NSArray(contentsOfFile: path!)
        return array as! [String]
    }()
    
    //标题数组
    lazy var titleArray:[String] = {
        var array = [String]()
        for str in self.cityDic.keys {
            array.append(str)
        }
        array.sort()
        array.insert("热门", at: 0)
        array.insert("定位", at: 0)
        return array
    }()
    
    //更新位置闭包
    var updateCityName:MyCityBlock?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationController?.navigationBar.tintColor = UIColor.white
        UIApplication.shared.statusBarStyle = .lightContent
        setupUI()
    }
    func setupUI(){
        //设置searchBar
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backBtn)
        self.navigationController?.navigationBar.addSubview(self.searchView)
        self.navigationController?.navigationBar.addSubview(cancleBtn)
        
        //设置导航条
        self.navigationController?.navbackgrouondImage = UIImage.createNVImage(by: Color.topNav)
        
        cityTBView.delegate = self
        cityTBView.dataSource = self
        cityTBView.register(UITableViewCell.self, forCellReuseIdentifier: normalCell)
        cityTBView.register(LocationCitiesCell.self, forCellReuseIdentifier: locaCityCell)
        cityTBView.register(HotCitiesCell.self, forCellReuseIdentifier: hotCityCell)
        
        //索引
        cityTBView.sectionIndexColor = Color.topNav
        cityTBView.sectionIndexBackgroundColor = Color.mainBg
        
        //Header View
        cityTBView.tableHeaderView = CurrentCityView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainWidth, height: 44))
        self.view.addSubview(cityTBView)
        self.view.addSubview(self.searchResultVC)
    }
    
    //MARK: 返回
    func backToHomeView(){
        self.dismiss(animated: true, completion: nil)
    }
    
    deinit {
        print("返回到首页")
    }
    override func viewWillLayoutSubviews() {
        self.cancleBtn.snp.makeConstraints { (make) in
            make.left.equalTo(self.searchView.snp.right).offset(5)
            make.top.bottom.equalToSuperview()
            make.width.equalTo(44)
        }
    }
    func endSearching(){

        SPAnimation.animate(0.5, animations: {
            self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: self.backBtn)
            self.searchView.frame = CGRect(x: UIScreen.mainWidth*0.2, y: 7, width: UIScreen.mainWidth*0.6, height: 30)
            self.searchBar.frame = CGRect(x: 30, y: 0, width: UIScreen.mainWidth*0.6-60, height: 30)
            self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: self.backBtn)
            self.searchBar.text = nil
            self.cancleBtn.isHidden = true
            self.searchResultVC.frame = CGRect(x: 0, y: -UIScreen.mainHeight, width: UIScreen.mainWidth, height: UIScreen.mainHeight)
        })
    }
    func beginSearching(){

        SPAnimation.animate(0.5, animations: {
            self.navigationItem.leftBarButtonItem = nil
            self.searchView.frame = CGRect(x: 15, y: 7, width: UIScreen.mainWidth*0.8-15, height: 30)
            self.searchBar.frame = CGRect(x: 5, y: 0, width: UIScreen.mainWidth*0.8-20, height: 30)
            self.cancleBtn.isHidden = false
            self.searchResultVC.frame = CGRect(x: 0, y: 0, width: UIScreen.mainWidth, height: UIScreen.mainHeight)
        })
    }
   
}

//MARK: - 搜索代理方法
extension CitySelectorViewController:UITextFieldDelegate{
    //开始输入时开始搜索
    func textFieldDidBeginEditing(_ textField: UITextField) {
       self.beginSearching()
    }
    //结束编辑
    func textFieldDidEndEditing(_ textField: UITextField) {
        self.getSearchResultArray(searchBarText: "")
    }
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let str = textField.text! as NSString
        
        let searhStr = str.replacingCharacters(in: range, with: string)
        self.getSearchResultArray(searchBarText: searhStr)
        return true
    }
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.view.endAllEditing()
        return true
    }
}

//MARK:- 城市列表的 代理方法  tableView
extension CitySelectorViewController:UITableViewDelegate,UITableViewDataSource{
    func numberOfSections(in tableView: UITableView) -> Int {
        return titleArray.count
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section > 1 {
            let key = titleArray[section]
            return cityDic[key]!.count - 3
        }
        return 1
    }
    
    // MARK: 创建cell
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if indexPath.section == 0 {
            
            let cell = tableView.dequeueReusableCell(withIdentifier: locaCityCell, for: indexPath) as! LocationCitiesCell
            return cell
        }else if indexPath.section == 1 {
            
            let cell = tableView.dequeueReusableCell(withIdentifier: hotCityCell, for: indexPath) as! HotCitiesCell
            cell.cityClicked = {(cityName:String) in
                self.updateCityName!(cityName)
                self.backToHomeView()
            }
            return cell
        }else {
            let cell = tableView.dequeueReusableCell(withIdentifier: normalCell, for: indexPath)
            // cell.backgroundColor = cellColor
            let key = titleArray[indexPath.section]
            cell.textLabel?.text = cityDic[key]![indexPath.row]
            return cell
        }
    }
    // MARK: 点击cell
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        if indexPath.section > 1 {
            tableView.deselectRow(at: indexPath, animated: false)
            let key = titleArray[indexPath.section]
            CurrentCity.shar.name = self.cityDic[key]![indexPath.row]
            updateCityName!(CurrentCity.shar.name)
            backToHomeView()
        }
    }
    
    // MARK: 右边索引
    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return titleArray
    }
    
    // MARK: section头视图
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainWidth+20, height: sectionMargin))
        view.backgroundColor = Color.mainBg
        let title = UILabel(frame: CGRect(x: 20, y: 5, width: UIScreen.mainWidth, height: 28))
        var titleArr = titleArray
        titleArr[0] = "定位城市"
        titleArr[1] = "热门城市"
        title.text = titleArr[section]
        title.textColor = Color.textM
        title.font = Font.largeP
        view.addSubview(title)
        
        if section > 1 {
            view.backgroundColor = Color.bgColor
            title.textColor = UIColor.darkGray
        }
        
        return view
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
      
        return sectionMargin
    }
    
    // MARK: row高度
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
       if indexPath.section == 0 {
            return btnHeight + 2 * btnMargin
        }else if indexPath.section == 1 {
            let row = (hotCities.count - 1) / 3
            return (btnHeight + 2 * btnMargin) + (btnMargin + btnHeight) * CGFloat(row)
        }else{
            return 42
        }
    }
    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        self.view.endEditing(true)
    }

}

//MARK: - 搜索逻辑
extension CitySelectorViewController{
    fileprivate func getSearchResultArray(searchBarText: String){
        var resultArray:[String] = []
        if searchBarText == "" {
            searchResultVC.resultArray = resultArray
            return
        }
        // 传递闭包 当点击’搜索结果‘的cell调用
        searchResultVC.updateCityName = {(cityName:String) in
            CurrentCity.shar.name = cityName
            self.updateCityName!(cityName)
            self.backToHomeView()
        }
        // 中文搜索

        if searchBarText.isIncludeChineseIn() {
            // 转拼音
            let pinyin = searchBarText.chineseToPinyin()
            // 获取大写首字母
            let first = String(pinyin[pinyin.startIndex]).uppercased()
            guard let dic = cityDic[first] else {
                return
            }
            for str in dic {
                if str.hasPrefix(searchBarText) {
                    resultArray.append(str)
                }
            }
            searchResultVC.resultArray = resultArray
        }else {
            // 拼音搜索
            // 若字符个数为1
            if searchBarText.characters.count == 1 {
                guard let dic = cityDic[searchBarText.uppercased()] else {
                    return
                }
                resultArray = dic
                searchResultVC.resultArray = resultArray
            }else {
                guard let dic = cityDic[searchBarText.first().uppercased()] else {
                    return
                }
                for str in dic {
                    // 去空格
                    let py = String(str.chineseToPinyin().characters.filter({ $0 != " "}))
                    let range = py.range(of: searchBarText)
                    if range != nil {
                        resultArray.append(str)
                    }
                }
                // 加入首字母判断 如 cq => 重庆 bj => 北京
                if resultArray.count == 0 {
                    for str in dic {
                        // 北京 => bei jing
                        let pinyin = str.chineseToPinyin()
                        // 获取空格的index
                        let a = pinyin.characters.index(of: " ")
                        let index = pinyin.index(a!, offsetBy: 2)
                        // offsetBy: 2 截取 bei j
                        // offsetBy: 1 截取 bei+空格
                        // substring(to: index) 不包含 index最后那个下标
                        let py = pinyin.substring(to: index)
                        /// 获取第二个首字母
                        ///
                        ///     py = "bei j"
                        ///     last = "j"
                        ///
                        let last = py.substring(from: py.index(py.endIndex, offsetBy: -1))
                        /// 两个首字母
                        let pyIndex = String(pinyin[pinyin.startIndex]) + last
                        
                        if searchBarText.lowercased() == pyIndex {
                            resultArray.append(str)
                        }
                    }
                }
                searchResultVC.resultArray = resultArray
            }
        }

    }
}


class CurrentCity:NSObject {
    static let shar = CurrentCity()
    
    fileprivate let path = ""
    var name:String{
        set{
            UserDefaults.standard.set(newValue, forKey: "city")
        }
        get{
            return UserDefaults.standard.string(forKey: "city") ?? "上海"
        }
    }
    override init() {
        super.init()
    }
}

搜索结果界面

fileprivate let resultCell = "resultCell"

import UIKit

//MARK: - 搜索结果页面
class ResultSearchView: BaseTableView{
    var resultArray:[String] = []{
        didSet{
            self.reloadData()
        }
    }
    
    var updateCityName:MyCityBlock?
    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        self.alertStr = "暂无搜索结果"
        self.setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func setupUI() {
        self.delegate = self
        self.dataSource = self
        self.backgroundColor = Color.mainBg
        self.register(UITableViewCell.self, forCellReuseIdentifier: resultCell)
    }
    
}
extension ResultSearchView:UITableViewDataSource,UITableViewDelegate{
    func numberOfSections(in tableView: UITableView) -> Int {
        
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return resultArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell =  tableView.dequeueReusableCell(withIdentifier: resultCell, for: indexPath)
        cell.textLabel?.text = resultArray[indexPath.row]
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at: indexPath)
        print(cell?.textLabel?.text ?? "")
        CurrentCity.shar.name = resultArray[indexPath.row]
        updateCityName!(CurrentCity.shar.name)
    }

}

自定义cell

let normalCell = "normalCell"
let hotCityCell = "hotCityCell"
let locaCityCell = "locaCityCell"
/// section间距
let sectionMargin: CGFloat = 38

/// 热门城市btn
let btnMargin: CGFloat = 15
let btnWidth: CGFloat = (UIScreen.mainWidth - 90) / 3
let btnHeight: CGFloat = 36

let cellColor = Color.mainBg
let btnHighlightImage = UIImage.createNVImage(by: UIColor.colorWithHexCode(code: "EAEAEA"))



import UIKit

//MARK: - 自定义cell
class LocationCitiesCell: UITableViewCell {
    lazy var locaView:UIView = {
        let locaV = UIView(frame: CGRect(x: btnMargin, y: 15, width: btnWidth, height: btnHeight))
        locaV.layer.masksToBounds = true
        locaV.layer.cornerRadius = 3
        locaV.backgroundColor = UIColor.white
        return locaV
    }()
    lazy var iconView:UIImageView = {
        let iconV = UIImageView(frame: CGRect(x: 5, y: 5, width: 20, height: 20))
        iconV.contentMode = .scaleAspectFit
        iconV.image = UIImage(named:"位置_H")
        return iconV
    }()
    lazy var titleLabel:UILabel = {
        let label = UILabel(frame: CGRect(x: 28, y: 5, width: btnWidth-28, height: btnHeight-10))
        label.textColor = Color.textM
        label.textAlignment = .center
        label.text = "定位城市"
        label.font = Font.large
        return label
    }()
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.setupUI()
        self.selectionStyle = UITableViewCellSelectionStyle.none
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        
        self.backgroundColor = cellColor
        locaView.addSubview(iconView)
        locaView.addSubview(titleLabel)
        self.addSubview(locaView)
        
    }
    
}
//当前:
class CurrentCityView: UIView {
    
    lazy var currentCityLabel:UILabel = {
        var label = UILabel(frame: CGRect(x: 15, y: 0, width: self.frame.width-15, height: self.frame.height))
        label.textColor = Color.textH
        label.font = Font.largePP
        label.backgroundColor = UIColor.white
        label.text = String(format: "当前:  %@", CurrentCity.shar.name)
        return label
    }()
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.white
        self.setupUI()
    }
    func setupUI(){
        self.addSubview(currentCityLabel)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class HotCitiesCell: UITableViewCell {
    /// 懒加载 热门城市
    var cityClicked:MyCityBlock?
    lazy var hotCities: [String] = {
        let path = Bundle.main.path(forResource: "hotCities.plist", ofType: nil)
        let array = NSArray(contentsOfFile: path!) as? [String]
        return array ?? []
    }()
    
    /// 使用tableView.dequeueReusableCell会自动调用这个方法
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.setupUI()
        self.selectionStyle = UITableViewCellSelectionStyle.none
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        
        self.backgroundColor = cellColor
        // 动态创建城市btn
        for i in 0..

调用方法:

 //FIXME: 选择地点
    func didLocationBtn(){
        self.locaBtn.setImage(#imageLiteral(resourceName: "位置_H"), for: .normal)

        let cityVC = CitySelectorViewController()
        cityVC.updateCityName = {(cityName:String) in
            self.locaCity.text = cityName
        }
        let locationNV = UINavigationController(rootViewController: cityVC)
        present(locationNV, animated: true) { 
            self.locaBtn.setImage(#imageLiteral(resourceName: "location"), for: .normal)
        }
    }

城市列表:

swift-城市搜索选择器_第1张图片
城市列表.png

效果图


swift-城市搜索选择器_第2张图片
button.png
swift-城市搜索选择器_第3张图片
搜索1.png
swift-城市搜索选择器_第4张图片
搜索2.png

你可能感兴趣的:(swift-城市搜索选择器)