镇楼专用
写这个的主要是为了练习Swift
的使用。
Plist文件内容
文件读取
fileprivate class WDAddressPickerDataManger: NSObject {
static let manager = WDAddressPickerDataManger()
// 读取list文件
func readPlistFile() -> Dictionary<String, Any> {
let path = Bundle.main.path(forResource: "list.plist", ofType: nil) ?? ""
let dict = NSDictionary(contentsOfFile: path)
if dict != nil {
return dict as! Dictionary<String, Any>
}
return [:]
}
// 读取所有省份
func getProvinceNameArray() -> Array<String> {
var array: [String] = Array()
for item in readPlistFile().keys {
array.append(item)
}
return array
}
// 读取普通省份中的城市
func getNormalCityNameArray(with provinceName: String) -> Array<String> {
var array: [String] = Array()
for item in readPlistFile() {
if item.key.contains(provinceName) {
let name = (item.value as! Dictionary<String, Any>).keys
for cityName in name {
array.append(cityName)
}
}
}
return array
}
// 读取直辖市,特区中的区
func getSpecailCityNameArray(with provinceName: String) -> Array<String> {
var array: [String] = Array()
for item in readPlistFile() {
if item.key.contains(provinceName) {
let name = item.value as! Array<String>
for cityName in name {
array.append(cityName)
}
}
}
return array
}
// 读取普通城市中的区
func getRegionNameArray(with provinceName: String, cityName: String) -> Array<String> {
var array: [String] = Array()
for item in readPlistFile() {
if item.key.contains(provinceName) {
let provinceDic = item.value as! Dictionary<String, Array<String>>
for city in provinceDic.keys {
if city.contains(cityName) {
for last in provinceDic[city]! {
array.append(last)
}
}
}
}
}
return array
}
}
HeaderView
提示文字,与页面消失按钮
fileprivate class WDAddressPickerHeaderView: UIView {
var textLabel: UILabel!
var clearButton: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSubviews() {
backgroundColor = .white
layer.cornerRadius = 10
textLabel = UILabel()
textLabel.text = "地址选择"
textLabel.textColor = .black
textLabel.font = .systemFont(ofSize: 20, weight: .bold)
addSubview(textLabel)
// 自定义按钮图片
clearButton = UIButton(type: .custom)
clearButton.setImage(UIImage(named: "clear"), for: .normal)
addSubview(clearButton)
}
override func layoutSubviews() {
super.layoutSubviews()
textLabel.frame = CGRect(x: 15, y: 0, width: bounds.width / 2.0, height: bounds.height)
let cycleWidth = (bounds.height - 10.0) / 2
clearButton.frame = CGRect(x: bounds.width - 15 - cycleWidth, y: (bounds.height - 10.0 - cycleWidth) / 2.0, width: cycleWidth, height: cycleWidth)
}
}
TopView
页面标题显示与切换
fileprivate class WDAddressPickerTopView: UIView {
// 点击回调
var didSelectedIndexBlock: ((_ index: Int) -> Void) = {(_) in}
// 标题底部指示器
var indicatorView: UIView!
// 标题之间的间距
let margin: CGFloat = 15
// 底部指示器的布局,用来进行布局修改与动画处理
var indicatorViewConstraints: [NSLayoutConstraint]!
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSubviews() {
backgroundColor = .white
indicatorView = UIView()
indicatorView.backgroundColor = .red
addSubview(indicatorView)
}
// for循环显示标题
func setup(with items: [String], selectIndex: Int) {
var lastButton = UIButton()
var newItems = items
if items.isEmpty {
newItems.append("请选择")
}
for view in subviews {
if view.isKind(of: UIButton.self) {
view.removeFromSuperview()
}
}
for (i, title) in newItems.enumerated() {
let button = UIButton(type: .custom)
button.setTitle(title, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 15, weight: .bold)
button.setTitleColor(.black, for: .normal)
button.addTarget(self, action: #selector(buttonTargetAction(sender:)), for: .touchUpInside)
button.tag = 1000 + I
addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
if i == 0 {
addConstraint(NSLayoutConstraint(item: button, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: margin))
} else {
addConstraint(NSLayoutConstraint(item: button, attribute: .leading, relatedBy: .equal, toItem: lastButton, attribute: .trailing, multiplier: 1, constant: margin))
}
addConstraints([
NSLayoutConstraint(item: button, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0)
])
lastButton = button
if i == selectIndex {
indicatorView.translatesAutoresizingMaskIntoConstraints = false
indicatorViewConstraints = [
NSLayoutConstraint(item: indicatorView, attribute: .leading, relatedBy: .equal, toItem: button, attribute: .leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: indicatorView, attribute: .trailing, relatedBy: .equal, toItem: button, attribute: .trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: indicatorView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: -3),
NSLayoutConstraint(item: indicatorView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 5),
]
addConstraints(indicatorViewConstraints)
}
}
}
@objc func buttonTargetAction(sender: UIButton) {
// 移除先前添加的布局
removeConstraints(indicatorViewConstraints)
indicatorViewConstraints = [
NSLayoutConstraint(item: indicatorView, attribute: .leading, relatedBy: .equal, toItem: sender, attribute: .leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: indicatorView, attribute: .trailing, relatedBy: .equal, toItem: sender, attribute: .trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: indicatorView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: -3),
NSLayoutConstraint(item: indicatorView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 5),
]
// 添加点击变化后的布局
addConstraints(indicatorViewConstraints)
setNeedsUpdateConstraints()
UIView.animate(withDuration: 0.3) {
self.layoutIfNeeded()
}
didSelectedIndexBlock(sender.tag - 1000)
}
}
列表单元格
fileprivate class WDAddressPickerTableViewCell: UITableViewCell {
var titleLabel: UILabel!
var checkedImageView: UIImageView!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSubviews() {
titleLabel = UILabel()
titleLabel.font = .systemFont(ofSize: 15)
titleLabel.textColor = .black
contentView.addSubview(titleLabel)
// 自定义显示图片
checkedImageView = UIImageView()
checkedImageView.image = UIImage(named: "duihao")
contentView.addSubview(checkedImageView)
}
private var _cellChecked: Bool = false
var cellChecked: Bool {
set {
_cellChecked = newValue
if !newValue {
checkedImageView.frame = CGRect(x: 15, y: 15, width: 0, height: 0)
titleLabel.frame = CGRect(x: 15, y: 0, width: UIScreen.main.bounds.width - 30, height: 45)
} else {
checkedImageView.frame = CGRect(x: 15, y: 15, width: 15, height: 15)
titleLabel.frame = CGRect(x: checkedImageView.frame.maxX + 15, y: 0, width: UIScreen.main.bounds.width - checkedImageView.frame.maxX - 15 - 15 , height: 45)
}
} get {
return _cellChecked
}
}
}
BottomView
底部列表视图载体,使用UIScrollView
嵌套UITableView
来实现
fileprivate class WDAddressPickerBottomView: UIView {
// Action
var didSelectedBlock: ((_ page: Int, _ index: Int, _ name: String) -> Void) = {(_,_,_) in }
// UI
var scrollView: UIScrollView!
var provinceTableView: UITableView!
var cityTableView: UITableView!
var regionTableView: UITableView!
// Data
var proVinceSelectedIndexRow = -1
var citySelectedIndexRow = -1
var regionSelectedIndexRow = -1
var selectedIndexRow = -1
var regionNameArray: [String] = Array()
var cityNameArray: [String] = Array()
var cityName: String = "" {
didSet {
regionSelectedIndexRow = -1
regionNameArray = WDAddressPickerDataManger.manager.getRegionNameArray(with: provinceName, cityName: cityName)
regionTableView.reloadData()
}
}
let provinceNameArray: [String] = WDAddressPickerDataManger.manager.getProvinceNameArray()
var provinceName: String = "" {
didSet {
if provinceName.contains("北京") ||
provinceName.contains("上海") ||
provinceName.contains("天津") ||
provinceName.contains("香港") ||
provinceName.contains("澳门") ||
provinceName.contains("重庆") {
cityNameArray = WDAddressPickerDataManger.manager.getSpecailCityNameArray(with: provinceName)
} else {
cityNameArray = WDAddressPickerDataManger.manager.getNormalCityNameArray(with: provinceName)
}
citySelectedIndexRow = -1
regionSelectedIndexRow = -1
cityTableView.reloadData()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
scrollView.frame = bounds
provinceTableView.frame = scrollView.bounds
cityTableView.frame = CGRect(x: scrollView.bounds.width, y: 0, width: scrollView.bounds.width, height: scrollView.bounds.height)
regionTableView.frame = CGRect(x: scrollView.bounds.width * 2.0, y: 0, width: scrollView.bounds.width, height: scrollView.bounds.height)
}
}
// MARK: Views Set Up
private extension WDAddressPickerBottomView {
func setupSubviews() {
scrollView = UIScrollView()
scrollView.isScrollEnabled = false
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.bounces = false
addSubview(scrollView)
provinceTableView = UITableView(frame: .zero, style: .plain)
provinceTableView.delegate = self
provinceTableView.dataSource = self
provinceTableView.rowHeight = 45
provinceTableView.register(WDAddressPickerTableViewCell.self, forCellReuseIdentifier: "WDAddressPickerTableViewCell")
scrollView.addSubview(provinceTableView)
cityTableView = UITableView(frame: .zero, style: .plain)
cityTableView.delegate = self
cityTableView.dataSource = self
cityTableView.rowHeight = 45
cityTableView.register(WDAddressPickerTableViewCell.self, forCellReuseIdentifier: "WDAddressPickerTableViewCell")
scrollView.addSubview(cityTableView)
regionTableView = UITableView(frame: .zero, style: .plain)
regionTableView.delegate = self
regionTableView.dataSource = self
regionTableView.rowHeight = 45
regionTableView.register(WDAddressPickerTableViewCell.self, forCellReuseIdentifier: "WDAddressPickerTableViewCell")
scrollView.addSubview(regionTableView)
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: 0)
}
}
// MARK: UITableView DataSource Methods
extension WDAddressPickerBottomView: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == provinceTableView {
return provinceNameArray.count
} else if tableView == cityTableView {
return cityNameArray.count
} else {
return regionNameArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "WDAddressPickerTableViewCell", for: indexPath) as! WDAddressPickerTableViewCell
if tableView == provinceTableView {
cell.titleLabel.text = (provinceNameArray[indexPath.row] as NSString).substring(from: 6)
if indexPath.row == proVinceSelectedIndexRow {
cell.cellChecked = true
} else {
cell.cellChecked = false
}
} else if tableView == cityTableView {
cell.titleLabel.text = (cityNameArray[indexPath.row] as NSString).substring(from: 6)
if indexPath.row == citySelectedIndexRow {
cell.cellChecked = true
} else {
cell.cellChecked = false
}
} else {
cell.titleLabel.text = (regionNameArray[indexPath.row] as NSString).substring(from: 6)
if indexPath.row == regionSelectedIndexRow {
cell.cellChecked = true
} else {
cell.cellChecked = false
}
}
return cell
}
}
// MARK: UITableView Delegate Method
extension WDAddressPickerBottomView: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let cell = tableView.cellForRow(at: indexPath) as! WDAddressPickerTableViewCell
if tableView == provinceTableView {
proVinceSelectedIndexRow = indexPath.row
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width * 2, height: 0)
scrollView.setContentOffset(CGPoint(x: UIScreen.main.bounds.width, y: 0), animated: true)
} else if tableView == cityTableView {
citySelectedIndexRow = indexPath.row
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width * 3, height: 0)
scrollView.setContentOffset(CGPoint(x: UIScreen.main.bounds.width * 2, y: 0), animated: true)
} else {
regionSelectedIndexRow = indexPath.row
}
tableView.reloadData()
selectedIndexRow = indexPath.row
didSelectedBlock(Int(scrollView.contentOffset.x / UIScreen.main.bounds.width), indexPath.row, cell.titleLabel.text ?? "")
}
}
AddressPickerView
以上的显示的视图的载体视图
fileprivate class WDAddressPickerView: UIView {
var headerView: WDAddressPickerHeaderView!
var topView: WDAddressPickerTopView!
var bottomView: WDAddressPickerBottomView!
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
setupSubviewsConstraint()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: 页面布局
@objc private extension WDAddressPickerView {
// 页面控件初始化
func setupSubviews() {
headerView = WDAddressPickerHeaderView()
addSubview(headerView)
topView = WDAddressPickerTopView()
topView.setup(with: [], selectIndex: 0)
addSubview(topView)
bottomView = WDAddressPickerBottomView()
addSubview(bottomView)
}
// 页面控件布局
func setupSubviewsConstraint() {
headerView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50)
topView.frame = CGRect(x: 0, y: headerView.frame.maxY - 10, width: UIScreen.main.bounds.width, height: 45)
bottomView.frame = CGRect(x: 0, y: topView.frame.maxY, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - 100 - topView.frame.maxY)
}
}
地址选取管理器
class WDAddressPickerManager: NSObject {
static let manager = WDAddressPickerManager()
private var backView: UIView!
private var addressPickerView: WDAddressPickerView!
/// 显示选择器
/// - Parameter completeBlock: province: 省(直辖市), city:市(直辖市中的区),region: 区
func showPicker(completeBlock: @escaping ((_ province: String, _ city: String, _ region: String) -> Void)) {
backView = UIView()
backView.backgroundColor = UIColor(white: 0, alpha: 0)
backView.frame = UIScreen.main.bounds
backView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGuestureTargetAction(sender:))))
UIApplication.shared.keyWindow?.addSubview(backView)
var titleItems: [String] = Array()
var provinceName = ""
var cityName = ""
var reginName = ""
addressPickerView = WDAddressPickerView()
addressPickerView.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - 100)
addressPickerView.headerView.clearButton.addTarget(self, action: #selector(tapGuestureTargetAction(sender:)), for: .touchUpInside)
addressPickerView.bottomView.didSelectedBlock = { [weak self] (page, index, name) in
if page == 0 {
provinceName = name
cityName = ""
reginName = ""
titleItems = [name, "请选择"]
self?.addressPickerView.bottomView.provinceName = name
self?.addressPickerView.topView.setup(with: titleItems, selectIndex: page + 1)
} else if page == 1 {
cityName = name
reginName = ""
if provinceName.contains("北京") ||
provinceName.contains("上海") ||
provinceName.contains("天津") ||
provinceName.contains("香港") ||
provinceName.contains("澳门") ||
provinceName.contains("重庆") {
self?.hidenView()
completeBlock(provinceName, cityName, reginName)
return
}
titleItems = [(provinceName), (cityName), "请选择"]
self?.addressPickerView.bottomView.cityName = name
self?.addressPickerView.topView.setup(with: titleItems, selectIndex: page + 1)
} else {
reginName = name
titleItems = [provinceName, cityName, reginName]
self?.addressPickerView.topView.setup(with: titleItems, selectIndex: page)
}
if page == 2 {
self?.hidenView()
completeBlock(provinceName, cityName, reginName)
}
}
addressPickerView.topView.didSelectedIndexBlock = { [weak self] index in
self?.addressPickerView.bottomView.scrollView.setContentOffset(CGPoint(x: UIScreen.main.bounds.width * CGFloat(index), y: 0), animated: true)
}
UIApplication.shared.keyWindow?.addSubview(addressPickerView)
UIView.animate(withDuration: 0.5) {
self.backView.backgroundColor = UIColor(white: 0, alpha: 0.3)
self.addressPickerView.frame = CGRect(x: 0, y: 100, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - 100)
}
}
@objc private func tapGuestureTargetAction(sender: Any) {
hidenView()
}
private func hidenView() {
UIView.animate(withDuration: 0.5, animations: {
self.backView.backgroundColor = UIColor(white: 0, alpha: 0)
self.addressPickerView.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - 100)
}) { (finished) in
self.backView.removeFromSuperview()
self.addressPickerView.removeFromSuperview()
}
}
}
效果图
效果.gif