Implement token-based root routing, Android-matched login screen, five-slot MainTabBar with scan action, and placeholder tab pages. Co-authored-by: Cursor <cursoragent@cursor.com>
115 lines
3.4 KiB
Swift
115 lines
3.4 KiB
Swift
//
|
||
// AgreementRowView.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import SnapKit
|
||
import UIKit
|
||
|
||
/// 协议勾选行,对应 Android `AgreementRow`。
|
||
final class AgreementRowView: UIView, UITextViewDelegate {
|
||
|
||
private let checkButton = UIButton(type: .custom)
|
||
private let textView = UITextView()
|
||
|
||
var isChecked = false {
|
||
didSet { updateCheckboxImage() }
|
||
}
|
||
|
||
var onCheckedChange: (() -> Void)?
|
||
var onUserAgreementTap: (() -> Void)?
|
||
var onPrivacyPolicyTap: (() -> Void)?
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
setupUI()
|
||
setupConstraints()
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
private func setupUI() {
|
||
checkButton.addTarget(self, action: #selector(checkButtonTapped), for: .touchUpInside)
|
||
updateCheckboxImage()
|
||
|
||
textView.backgroundColor = .clear
|
||
textView.isEditable = false
|
||
textView.isScrollEnabled = false
|
||
textView.textContainerInset = .zero
|
||
textView.textContainer.lineFragmentPadding = 0
|
||
textView.delegate = self
|
||
textView.linkTextAttributes = [
|
||
.foregroundColor: AppColor.agreementLink,
|
||
.font: UIFont.systemFont(ofSize: 12, weight: .semibold),
|
||
]
|
||
textView.attributedText = makeAgreementText()
|
||
|
||
addSubview(checkButton)
|
||
addSubview(textView)
|
||
}
|
||
|
||
private func setupConstraints() {
|
||
checkButton.snp.makeConstraints { make in
|
||
make.leading.top.equalToSuperview()
|
||
make.size.equalTo(18)
|
||
}
|
||
|
||
textView.snp.makeConstraints { make in
|
||
make.leading.equalTo(checkButton.snp.trailing).offset(8)
|
||
make.trailing.top.bottom.equalToSuperview()
|
||
}
|
||
}
|
||
|
||
private func updateCheckboxImage() {
|
||
let imageName = isChecked ? "icon_cb_privacy_selected" : "icon_cb_privacy_unselect"
|
||
checkButton.setImage(UIImage(named: imageName), for: .normal)
|
||
}
|
||
|
||
private func makeAgreementText() -> NSAttributedString {
|
||
let result = NSMutableAttributedString()
|
||
let normalAttributes: [NSAttributedString.Key: Any] = [
|
||
.font: UIFont.systemFont(ofSize: 12),
|
||
.foregroundColor: AppColor.text666,
|
||
]
|
||
let linkFont = UIFont.systemFont(ofSize: 12, weight: .semibold)
|
||
|
||
result.append(NSAttributedString(string: "已阅读并同意", attributes: normalAttributes))
|
||
result.append(NSAttributedString(string: "《用户协议》", attributes: [
|
||
.font: linkFont,
|
||
.foregroundColor: AppColor.agreementLink,
|
||
.link: "tos",
|
||
]))
|
||
result.append(NSAttributedString(string: "与", attributes: normalAttributes))
|
||
result.append(NSAttributedString(string: "《隐私政策》", attributes: [
|
||
.font: linkFont,
|
||
.foregroundColor: AppColor.agreementLink,
|
||
.link: "privacy",
|
||
]))
|
||
return result
|
||
}
|
||
|
||
@objc private func checkButtonTapped() {
|
||
onCheckedChange?()
|
||
}
|
||
|
||
func textView(
|
||
_ textView: UITextView,
|
||
shouldInteractWith URL: URL,
|
||
in characterRange: NSRange,
|
||
interaction: UITextItemInteraction
|
||
) -> Bool {
|
||
switch URL.absoluteString {
|
||
case "tos":
|
||
onUserAgreementTap?()
|
||
case "privacy":
|
||
onPrivacyPolicyTap?()
|
||
default:
|
||
break
|
||
}
|
||
return false
|
||
}
|
||
}
|