代码:
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)
}
}
城市列表:
效果图