346 lines
10 KiB
Swift
346 lines
10 KiB
Swift
|
|
//
|
||
|
|
// SelectCommonController.swift
|
||
|
|
// Crush
|
||
|
|
//
|
||
|
|
// Created by Leon on 2025/7/25.
|
||
|
|
//
|
||
|
|
|
||
|
|
import SnapKit
|
||
|
|
import UIKit
|
||
|
|
|
||
|
|
protocol SelectCommonModelProtocol: AnyObject {
|
||
|
|
func displayStr() -> String
|
||
|
|
|
||
|
|
func sourceObj() -> Any
|
||
|
|
}
|
||
|
|
|
||
|
|
class SelectCommonController: CLBaseViewController {
|
||
|
|
// MARK: - Properties from header
|
||
|
|
|
||
|
|
var vcTitle: String?
|
||
|
|
var isMultiChoose: Bool = false
|
||
|
|
var atlastSelectOne: Bool = false
|
||
|
|
var cellClass: UITableViewCell.Type?
|
||
|
|
var datas: [SelectCommonModelProtocol] = []
|
||
|
|
var markSelected: [SelectCommonModelProtocol] = []
|
||
|
|
var tapSingleAction: ((SelectCommonModelProtocol) -> Void)?
|
||
|
|
var tapMultiConfrimAction: (([SelectCommonModelProtocol]) -> Void)?
|
||
|
|
var specialCellForSignInFirstOption: SelectCommonModelProtocol?
|
||
|
|
|
||
|
|
// MARK: - Private Properties
|
||
|
|
|
||
|
|
private lazy var titleView: TitleView = {
|
||
|
|
let view = TitleView()
|
||
|
|
return view
|
||
|
|
}()
|
||
|
|
|
||
|
|
private lazy var tableView: UITableView = {
|
||
|
|
let tableView = UITableView()
|
||
|
|
tableView.separatorStyle = .none
|
||
|
|
tableView.backgroundColor = .clear
|
||
|
|
tableView.delegate = self
|
||
|
|
tableView.dataSource = self
|
||
|
|
tableView.contentInsetAdjustmentBehavior = .never
|
||
|
|
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: UIWindow.safeAreaBottom, right: 0)
|
||
|
|
|
||
|
|
if let cellClass = cellClass {
|
||
|
|
tableView.register(cellClass, forCellReuseIdentifier: String(describing: cellClass))
|
||
|
|
} else {
|
||
|
|
tableView.register(SelectCommonTableCell.self, forCellReuseIdentifier: String(describing: SelectCommonTableCell.self))
|
||
|
|
}
|
||
|
|
|
||
|
|
return tableView
|
||
|
|
}()
|
||
|
|
|
||
|
|
private var tempSelectModels: [SelectCommonModelProtocol] = []
|
||
|
|
|
||
|
|
// MARK: - Lifecycle
|
||
|
|
|
||
|
|
override func viewDidLoad() {
|
||
|
|
super.viewDidLoad()
|
||
|
|
setupUI()
|
||
|
|
setupData()
|
||
|
|
setupEvents()
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Setup
|
||
|
|
|
||
|
|
private func setupUI() {
|
||
|
|
title = "Select"
|
||
|
|
|
||
|
|
navigationView.setupBackButtonCloseIcon()
|
||
|
|
|
||
|
|
view.addSubview(tableView)
|
||
|
|
tableView.snp.makeConstraints { make in
|
||
|
|
make.top.equalTo(navigationView.snp.bottom)
|
||
|
|
make.left.right.bottom.equalToSuperview()
|
||
|
|
}
|
||
|
|
|
||
|
|
titleView.title = vcTitle
|
||
|
|
titleView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: titleView.preCalculateHeight())
|
||
|
|
tableView.tableHeaderView = titleView
|
||
|
|
|
||
|
|
if isMultiChoose {
|
||
|
|
navigationView.styleMainButton.isHidden = false
|
||
|
|
navigationView.styleMainButton.setTitle("Confirm", for: .normal)
|
||
|
|
refreshMultiChooseConfirmBtn()
|
||
|
|
}
|
||
|
|
|
||
|
|
view.setNeedsDisplay()
|
||
|
|
view.layoutIfNeeded()
|
||
|
|
}
|
||
|
|
|
||
|
|
private func tapNaviConfirm() {
|
||
|
|
tapMultiConfrimAction?(tempSelectModels)
|
||
|
|
markSelected = tempSelectModels
|
||
|
|
dismiss(animated: true, completion: nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
private func setupData() {
|
||
|
|
if markSelected.count == 1, let firstSelected = markSelected.first,
|
||
|
|
let index = datas.firstIndex(where: { $0 === firstSelected }) {
|
||
|
|
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: false)
|
||
|
|
}
|
||
|
|
|
||
|
|
tempSelectModels = markSelected.isEmpty ? [] : markSelected
|
||
|
|
}
|
||
|
|
|
||
|
|
private func setupEvents() {
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Class Methods
|
||
|
|
|
||
|
|
static func navController() -> CLNavigationController {
|
||
|
|
let vc = SelectCommonController()
|
||
|
|
return CLNavigationController(rootViewController: vc)
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Helpers
|
||
|
|
|
||
|
|
private func refreshMultiChooseConfirmBtn() {
|
||
|
|
guard isMultiChoose else {
|
||
|
|
assertionFailure("Should not call refreshMultiChooseConfirmBtn when not in multi-choose mode")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
navigationView.styleMainButton.isEnabled = atlastSelectOne ? !tempSelectModels.isEmpty : true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - UITableViewDelegate, UITableViewDataSource
|
||
|
|
|
||
|
|
extension SelectCommonController: UITableViewDelegate, UITableViewDataSource {
|
||
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||
|
|
return datas.count
|
||
|
|
}
|
||
|
|
|
||
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||
|
|
let data = datas[indexPath.row]
|
||
|
|
|
||
|
|
if let cellClass = cellClass as? SelectCommonBaseCell.Type {
|
||
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: cellClass), for: indexPath) as! SelectCommonBaseCell
|
||
|
|
cell.config(data: data)
|
||
|
|
cell.configSelect(selected: markSelected.contains { $0 === data })
|
||
|
|
return cell
|
||
|
|
} else {
|
||
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SelectCommonTableCell.self), for: indexPath) as! SelectCommonTableCell
|
||
|
|
cell.isMultiChoose = isMultiChoose
|
||
|
|
cell.config(data: data)
|
||
|
|
cell.configSelect(selected: markSelected.contains { $0 === data })
|
||
|
|
|
||
|
|
if let specialOption = specialCellForSignInFirstOption, specialOption === data {
|
||
|
|
cell.configSignInFirstMode(true)
|
||
|
|
} else {
|
||
|
|
cell.configSignInFirstMode(false)
|
||
|
|
}
|
||
|
|
return cell
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||
|
|
let data = datas[indexPath.row]
|
||
|
|
|
||
|
|
if specialCellForSignInFirstOption != nil && specialCellForSignInFirstOption === data {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if isMultiChoose {
|
||
|
|
if let index = tempSelectModels.firstIndex(where: { $0 === data }) {
|
||
|
|
tempSelectModels.remove(at: index)
|
||
|
|
} else {
|
||
|
|
tempSelectModels.append(data)
|
||
|
|
}
|
||
|
|
markSelected = tempSelectModels
|
||
|
|
tableView.reloadData()
|
||
|
|
refreshMultiChooseConfirmBtn()
|
||
|
|
} else {
|
||
|
|
markSelected = [data]
|
||
|
|
tapSingleAction?(data)
|
||
|
|
dismiss(animated: true, completion: nil)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||
|
|
if !(vcTitle?.isEmpty ?? true) {
|
||
|
|
NaviAlphaHandle.changeNaviTitleAlpha(scrollView: scrollView, titleLabel: navigationView.titleLabel)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - SelectCommonModel
|
||
|
|
|
||
|
|
class SelectCommonModel: SelectCommonModelProtocol {
|
||
|
|
var name: String?
|
||
|
|
var objInfo: Any?
|
||
|
|
var integerInfo: Int = 0
|
||
|
|
|
||
|
|
func displayStr() -> String {
|
||
|
|
return name ?? ""
|
||
|
|
}
|
||
|
|
|
||
|
|
func sourceObj() -> Any {
|
||
|
|
return objInfo as Any
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class SelectCommonBaseCell: UITableViewCell {
|
||
|
|
override func awakeFromNib() {
|
||
|
|
super.awakeFromNib()
|
||
|
|
// Initialization code
|
||
|
|
}
|
||
|
|
|
||
|
|
override func setSelected(_ selected: Bool, animated: Bool) {
|
||
|
|
super.setSelected(selected, animated: animated)
|
||
|
|
// Configure the view for the selected state
|
||
|
|
}
|
||
|
|
|
||
|
|
func config(data: SelectCommonModelProtocol) {
|
||
|
|
// Base implementation, to be overridden by subclasses
|
||
|
|
}
|
||
|
|
|
||
|
|
func configSelect(selected: Bool) {
|
||
|
|
// Base implementation, to be overridden by subclasses
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class SelectCommonTableCell: SelectCommonBaseCell {
|
||
|
|
// MARK: - Properties
|
||
|
|
|
||
|
|
private lazy var label: UILabel = {
|
||
|
|
let label = UILabel()
|
||
|
|
label.font = .t.tbl // EPSystemToken.typography(.bodyL).font
|
||
|
|
label.numberOfLines = 0
|
||
|
|
return label
|
||
|
|
}()
|
||
|
|
|
||
|
|
private lazy var checkButton: EPRadioButton = {
|
||
|
|
let button = EPRadioButton()
|
||
|
|
button.isUserInteractionEnabled = false
|
||
|
|
button.isSelected = true
|
||
|
|
return button
|
||
|
|
}()
|
||
|
|
|
||
|
|
private lazy var line: UIView = {
|
||
|
|
let view = UIView()
|
||
|
|
return view
|
||
|
|
}()
|
||
|
|
|
||
|
|
private lazy var signInFirstTipLabel: UILabel = {
|
||
|
|
let label = UILabel()
|
||
|
|
label.textColor = .c.cttn
|
||
|
|
label.font = .t.tbs
|
||
|
|
label.text = "Please Sign In"
|
||
|
|
label.isHidden = true
|
||
|
|
return label
|
||
|
|
}()
|
||
|
|
|
||
|
|
private var signInFirstMode: Bool = false
|
||
|
|
|
||
|
|
var isMultiChoose: Bool = false {
|
||
|
|
didSet {
|
||
|
|
if isMultiChoose {
|
||
|
|
checkButton.setupStyle(.rectangle)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
|
|
contentView.backgroundColor = .clear
|
||
|
|
backgroundColor = .clear
|
||
|
|
selectionStyle = .none
|
||
|
|
initialViews()
|
||
|
|
}
|
||
|
|
|
||
|
|
required init?(coder: NSCoder) {
|
||
|
|
fatalError("init(coder:) has not been implemented")
|
||
|
|
}
|
||
|
|
|
||
|
|
override func awakeFromNib() {
|
||
|
|
super.awakeFromNib()
|
||
|
|
// Initialization code
|
||
|
|
}
|
||
|
|
|
||
|
|
override func setSelected(_ selected: Bool, animated: Bool) {
|
||
|
|
super.setSelected(selected, animated: animated)
|
||
|
|
// Configure the view for the selected state
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Setup
|
||
|
|
|
||
|
|
private func initialViews() {
|
||
|
|
contentView.addSubview(checkButton)
|
||
|
|
contentView.addSubview(label)
|
||
|
|
contentView.addSubview(line)
|
||
|
|
contentView.addSubview(signInFirstTipLabel)
|
||
|
|
|
||
|
|
checkButton.snp.makeConstraints { make in
|
||
|
|
make.size.equalTo(CGSize(width: 20, height: 20))
|
||
|
|
make.right.equalToSuperview().offset(-24)
|
||
|
|
make.centerY.equalToSuperview()
|
||
|
|
}
|
||
|
|
|
||
|
|
label.snp.makeConstraints { make in
|
||
|
|
make.left.equalToSuperview().offset(24)
|
||
|
|
make.top.equalToSuperview().offset(24)
|
||
|
|
make.bottom.equalToSuperview().offset(-24)
|
||
|
|
make.right.lessThanOrEqualTo(checkButton.snp.left).offset(-16)
|
||
|
|
}
|
||
|
|
|
||
|
|
line.snp.makeConstraints { make in
|
||
|
|
make.leading.equalToSuperview().offset(24)
|
||
|
|
make.trailing.equalToSuperview().offset(-24)
|
||
|
|
make.bottom.equalToSuperview()
|
||
|
|
make.height.equalTo(0.5)
|
||
|
|
}
|
||
|
|
|
||
|
|
signInFirstTipLabel.snp.makeConstraints { make in
|
||
|
|
make.centerY.equalToSuperview()
|
||
|
|
make.right.equalToSuperview().offset(-24)
|
||
|
|
}
|
||
|
|
|
||
|
|
label.textColor = .c.ctpn
|
||
|
|
line.backgroundColor = .c.con
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Public Methods
|
||
|
|
|
||
|
|
override func config(data: SelectCommonModelProtocol) {
|
||
|
|
label.text = data.displayStr()
|
||
|
|
}
|
||
|
|
|
||
|
|
override func configSelect(selected: Bool) {
|
||
|
|
checkButton.isHidden = !selected
|
||
|
|
}
|
||
|
|
|
||
|
|
func configSignInFirstMode(_ mode: Bool) {
|
||
|
|
signInFirstMode = mode
|
||
|
|
if mode {
|
||
|
|
label.textColor = .c.cttn
|
||
|
|
signInFirstTipLabel.isHidden = false
|
||
|
|
} else {
|
||
|
|
label.textColor = .c.ctpn
|
||
|
|
signInFirstTipLabel.isHidden = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|