Visual_Novel_iOS/crush/Crush/Src/Components/UI/Picker/SelectCommonController.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
}
}
}