Files
suixinkan_uikit/suixinkan/UI/Setting/SettingViewController.swift
汉秋 6336feff27 完善景区排队设置页与全局按钮配置迁移。
重构排队设置与变更日志交互,补充 Overlay 与资源,并将多页面主操作按钮统一到 UIButton Configuration,同步更新相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-14 16:21:53 +08:00

231 lines
7.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SettingViewController.swift
// suixinkan
//
import SnapKit
import UIKit
/// Android `SettingScreen`
final class SettingViewController: BaseViewController {
private let viewModel: SettingViewModel
private let settingAPI: SettingAPI
private let contentView = UIView()
private let cardView = UIView()
private let rowsStack = UIStackView()
private let versionRow = SettingMenuRow(title: "系统版本", showsChevron: false)
private let copyrightLabel = UILabel()
init(
viewModel: SettingViewModel = SettingViewModel(),
settingAPI: SettingAPI? = nil
) {
self.viewModel = viewModel
self.settingAPI = settingAPI ?? NetworkServices.shared.settingAPI
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setupNavigationBar() {
title = "设置"
}
override func setupUI() {
view.backgroundColor = AppColor.pageBackground
view.addSubview(contentView)
contentView.addSubview(cardView)
cardView.addSubview(rowsStack)
contentView.addSubview(copyrightLabel)
cardView.backgroundColor = .white
cardView.layer.cornerRadius = 12
cardView.clipsToBounds = true
rowsStack.axis = .vertical
copyrightLabel.text = "Copyright © 2025 All Rights Reserved\n苏ICP备2025157647号"
copyrightLabel.font = .systemFont(ofSize: 14)
copyrightLabel.textColor = UIColor(hex: 0xB6BECA)
copyrightLabel.textAlignment = .center
copyrightLabel.numberOfLines = 0
let rows: [SettingMenuRow] = [
SettingMenuRow(title: "关于我们"),
versionRow,
SettingMenuRow(title: "App下载", value: "复制链接", valueColor: AppColor.primary, showsChevron: false),
SettingMenuRow(title: "用户协议"),
SettingMenuRow(title: "隐私政策", showsDivider: false),
]
rows.forEach(rowsStack.addArrangedSubview)
rows[0].addTarget(self, action: #selector(aboutTapped), for: .touchUpInside)
rows[2].addTarget(self, action: #selector(copyDownloadTapped), for: .touchUpInside)
rows[3].addTarget(self, action: #selector(userAgreementTapped), for: .touchUpInside)
rows[4].addTarget(self, action: #selector(privacyTapped), for: .touchUpInside)
}
override func setupConstraints() {
contentView.snp.makeConstraints { make in
make.edges.equalTo(view.safeAreaLayoutGuide).inset(16)
}
cardView.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
}
rowsStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16))
}
copyrightLabel.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
make.bottom.equalToSuperview().offset(-16)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.applyViewModel() }
}
}
override func viewDidLoad() {
super.viewDidLoad()
applyViewModel()
Task { await viewModel.checkLatestVersion(api: settingAPI) }
}
private func applyViewModel() {
versionRow.configure(value: viewModel.appVersion, showsUpgrade: viewModel.hasNewVersion)
}
@objc private func aboutTapped() {
openAgreement(.aboutUs)
}
@objc private func copyDownloadTapped() {
UIPasteboard.general.string = viewModel.appDownloadURL.absoluteString
showToast("已复制到剪贴板!")
}
@objc private func userAgreementTapped() {
openAgreement(.userAgreement)
}
@objc private func privacyTapped() {
openAgreement(.privacyPolicy)
}
private func openAgreement(_ kind: SettingAgreementKind) {
let destination = viewModel.agreementDestination(for: kind)
navigationController?.pushViewController(
AgreementWebViewController(title: destination.title, url: destination.url),
animated: true
)
}
}
///
final class SettingMenuRow: UIControl {
private let titleLabel = UILabel()
private let rightStack = UIStackView()
private let valueLabel = UILabel()
private let upgradeImageView = UIImageView()
private let chevronImageView = UIImageView()
private let divider = UIView()
private let showsChevron: Bool
init(
title: String,
value: String? = nil,
valueColor: UIColor = AppColor.textPrimary,
showsChevron: Bool = true,
showsDivider: Bool = true
) {
self.showsChevron = showsChevron
super.init(frame: .zero)
setupUI()
setupConstraints()
titleLabel.text = title
valueLabel.text = value
valueLabel.textColor = valueColor
chevronImageView.isHidden = !showsChevron
divider.isHidden = !showsDivider
accessibilityTraits.insert(.button)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var isHighlighted: Bool {
didSet {
backgroundColor = isHighlighted ? UIColor.black.withAlphaComponent(0.04) : .clear
}
}
func configure(value: String?, showsUpgrade: Bool) {
valueLabel.text = value
upgradeImageView.isHidden = !showsUpgrade
}
private func setupUI() {
titleLabel.font = .systemFont(ofSize: 14)
titleLabel.textColor = UIColor(hex: 0x4B5563)
valueLabel.font = .systemFont(ofSize: 14)
valueLabel.textColor = AppColor.textPrimary
upgradeImageView.image = UIImage(systemName: "arrow.up.circle")?.withRenderingMode(.alwaysTemplate)
upgradeImageView.tintColor = UIColor(hex: 0x1F1F1F)
upgradeImageView.contentMode = .scaleAspectFit
upgradeImageView.isHidden = true
chevronImageView.image = UIImage(systemName: "chevron.right")?.withRenderingMode(.alwaysTemplate)
chevronImageView.tintColor = AppColor.textTertiary
chevronImageView.contentMode = .scaleAspectFit
divider.backgroundColor = AppColor.border
rightStack.axis = .horizontal
rightStack.alignment = .center
rightStack.spacing = 4
[valueLabel, upgradeImageView, chevronImageView].forEach(rightStack.addArrangedSubview)
[titleLabel, rightStack, divider].forEach(addSubview)
}
private func setupConstraints() {
snp.makeConstraints { make in
make.height.equalTo(52)
}
titleLabel.snp.makeConstraints { make in
make.leading.centerY.equalToSuperview()
}
upgradeImageView.snp.makeConstraints { make in
make.width.height.equalTo(24)
}
chevronImageView.snp.makeConstraints { make in
make.width.height.equalTo(24)
}
rightStack.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.trailing.equalToSuperview()
make.leading.greaterThanOrEqualTo(titleLabel.snp.trailing).offset(12)
}
divider.snp.makeConstraints { make in
make.leading.trailing.bottom.equalToSuperview()
make.height.equalTo(0.5)
}
}
}