添加景区入驻申请流程并对齐 Android
This commit is contained in:
@ -0,0 +1,684 @@
|
||||
//
|
||||
// ScenicApplicationViews.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import Kingfisher
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
/// 温馨提示卡片,对齐 Android `WarmReminderCard`。
|
||||
final class ScenicApplicationWarmReminderView: UIView {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = AppColor.primaryBanner
|
||||
layer.cornerRadius = 8
|
||||
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.text = "温馨提示"
|
||||
titleLabel.font = .systemFont(ofSize: 16, weight: .bold)
|
||||
titleLabel.textColor = .white
|
||||
|
||||
let bodyLabel = UILabel()
|
||||
bodyLabel.text = "如果你想入驻更多的景区,平台可以帮你跟景区官方谈合作,如果已经有的景区,只要你符合入驻的条件,即可入驻。"
|
||||
bodyLabel.font = .systemFont(ofSize: 14)
|
||||
bodyLabel.textColor = .white
|
||||
bodyLabel.numberOfLines = 0
|
||||
|
||||
let stack = UIStackView(arrangedSubviews: [titleLabel, bodyLabel])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
addSubview(stack)
|
||||
stack.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(16)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
/// 审核状态卡片,对齐 Android `ScenicApplicationAuditStatusCard`。
|
||||
final class ScenicApplicationAuditStatusView: UIView {
|
||||
|
||||
func apply(pending: ScenicApplicationPendingResponse) {
|
||||
subviews.forEach { $0.removeFromSuperview() }
|
||||
|
||||
let statusLabel: String
|
||||
let textColor: UIColor
|
||||
let background: UIColor
|
||||
switch pending.status {
|
||||
case 1:
|
||||
statusLabel = "待审核"
|
||||
textColor = AppColor.warning
|
||||
background = AppColor.warningBackground
|
||||
case 2:
|
||||
statusLabel = "通过"
|
||||
textColor = AppColor.info
|
||||
background = AppColor.primaryLight
|
||||
case 3, 9:
|
||||
statusLabel = pending.status == 3 ? "拒绝" : "取消"
|
||||
textColor = AppColor.danger
|
||||
background = AppColor.dangerBackground
|
||||
default:
|
||||
statusLabel = "--"
|
||||
textColor = AppColor.warning
|
||||
background = AppColor.warningBackground
|
||||
}
|
||||
|
||||
backgroundColor = background
|
||||
|
||||
let stack = UIStackView()
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
|
||||
stack.addArrangedSubview(makeRow(label: "审核状态", value: statusLabel, color: textColor))
|
||||
if pending.status == 2 {
|
||||
stack.addArrangedSubview(makeRow(label: "审核人", value: pending.auditedBy ?? "--", color: textColor))
|
||||
stack.addArrangedSubview(makeRow(label: "审核时间", value: pending.auditedAt ?? "--", color: textColor))
|
||||
}
|
||||
let note = pending.rejectReason ?? pending.auditNote ?? "--"
|
||||
stack.addArrangedSubview(makeRow(label: "备注", value: note.isEmpty ? "--" : note, color: textColor))
|
||||
|
||||
addSubview(stack)
|
||||
stack.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20))
|
||||
}
|
||||
}
|
||||
|
||||
private func makeRow(label: String, value: String, color: UIColor) -> UIView {
|
||||
let row = UIStackView()
|
||||
row.axis = .horizontal
|
||||
row.distribution = .equalSpacing
|
||||
|
||||
let left = UILabel()
|
||||
left.text = label
|
||||
left.font = .systemFont(ofSize: 14)
|
||||
left.textColor = color
|
||||
|
||||
let right = UILabel()
|
||||
right.text = value
|
||||
right.font = .systemFont(ofSize: 14)
|
||||
right.textColor = color
|
||||
right.textAlignment = .right
|
||||
right.numberOfLines = 0
|
||||
|
||||
row.addArrangedSubview(left)
|
||||
row.addArrangedSubview(right)
|
||||
return row
|
||||
}
|
||||
}
|
||||
|
||||
/// 表单字段标题 + 内容容器。
|
||||
final class ScenicApplicationFormFieldView: UIView {
|
||||
|
||||
private let titleLabel = UILabel()
|
||||
private let contentContainer = UIView()
|
||||
|
||||
init(title: String) {
|
||||
super.init(frame: .zero)
|
||||
titleLabel.text = title
|
||||
titleLabel.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
titleLabel.textColor = AppColor.textPrimary
|
||||
|
||||
let stack = UIStackView(arrangedSubviews: [titleLabel, contentContainer])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
addSubview(stack)
|
||||
stack.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setContent(_ view: UIView) {
|
||||
contentContainer.subviews.forEach { $0.removeFromSuperview() }
|
||||
contentContainer.addSubview(view)
|
||||
view.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
func updateTitle(_ title: String) {
|
||||
titleLabel.text = title
|
||||
}
|
||||
}
|
||||
|
||||
/// 省/市选择按钮。
|
||||
final class ScenicApplicationLocationSelector: UIControl {
|
||||
|
||||
private let valueLabel = UILabel()
|
||||
private let chevron = UIImageView(image: UIImage(systemName: "chevron.down"))
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .white
|
||||
layer.cornerRadius = 8
|
||||
layer.borderWidth = 1
|
||||
layer.borderColor = AppColor.border.cgColor
|
||||
|
||||
valueLabel.font = .systemFont(ofSize: 14)
|
||||
valueLabel.textColor = AppColor.textTertiary
|
||||
valueLabel.text = "请选择"
|
||||
|
||||
chevron.tintColor = AppColor.textTertiary
|
||||
chevron.contentMode = .scaleAspectFit
|
||||
|
||||
addSubview(valueLabel)
|
||||
addSubview(chevron)
|
||||
valueLabel.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(12)
|
||||
make.centerY.equalToSuperview()
|
||||
make.trailing.lessThanOrEqualTo(chevron.snp.leading).offset(-8)
|
||||
}
|
||||
chevron.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview().offset(-12)
|
||||
make.centerY.equalToSuperview()
|
||||
make.size.equalTo(16)
|
||||
}
|
||||
snp.makeConstraints { make in
|
||||
make.height.equalTo(44)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setValue(_ value: String?, placeholder: String) {
|
||||
if let value, !value.isEmpty {
|
||||
valueLabel.text = value
|
||||
valueLabel.textColor = AppColor.textPrimary
|
||||
} else {
|
||||
valueLabel.text = placeholder
|
||||
valueLabel.textColor = AppColor.textTertiary
|
||||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Bool {
|
||||
didSet {
|
||||
alpha = isEnabled ? 1 : 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 合作类型单选项。
|
||||
final class ScenicApplicationCooperationOptionView: UIControl {
|
||||
|
||||
private let titleLabel = UILabel()
|
||||
|
||||
override var isSelected: Bool {
|
||||
didSet { applyStyle() }
|
||||
}
|
||||
|
||||
init(title: String) {
|
||||
super.init(frame: .zero)
|
||||
titleLabel.text = title
|
||||
titleLabel.font = .systemFont(ofSize: 14)
|
||||
titleLabel.numberOfLines = 0
|
||||
layer.cornerRadius = 8
|
||||
layer.borderWidth = 2
|
||||
|
||||
addSubview(titleLabel)
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(16)
|
||||
}
|
||||
applyStyle()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func applyStyle() {
|
||||
if isSelected {
|
||||
backgroundColor = AppColor.primaryBanner
|
||||
titleLabel.textColor = .white
|
||||
layer.borderColor = UIColor.clear.cgColor
|
||||
} else {
|
||||
backgroundColor = .white
|
||||
titleLabel.textColor = AppColor.textSecondary
|
||||
layer.borderColor = AppColor.inputBackground.cgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 横向图片上传区域。
|
||||
final class ScenicApplicationImageUploadView: UIView {
|
||||
|
||||
var onAdd: (() -> Void)?
|
||||
var onDelete: ((Int) -> Void)?
|
||||
var onRetry: ((Int) -> Void)?
|
||||
|
||||
private let scrollView = UIScrollView()
|
||||
private let stackView = UIStackView()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = 8
|
||||
stackView.alignment = .center
|
||||
scrollView.showsHorizontalScrollIndicator = false
|
||||
scrollView.addSubview(stackView)
|
||||
addSubview(scrollView)
|
||||
|
||||
scrollView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
make.height.equalTo(80)
|
||||
}
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
make.height.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func apply(images: [ScenicApplicationImageItem], isReadOnly: Bool) {
|
||||
stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||
|
||||
for (index, item) in images.enumerated() {
|
||||
let cell = ScenicApplicationImageCell()
|
||||
cell.apply(item: item, showDelete: !isReadOnly)
|
||||
cell.onDelete = { [weak self] in self?.onDelete?(index) }
|
||||
cell.onRetry = { [weak self] in self?.onRetry?(index) }
|
||||
stackView.addArrangedSubview(cell)
|
||||
cell.snp.makeConstraints { make in
|
||||
make.size.equalTo(80)
|
||||
}
|
||||
}
|
||||
|
||||
if images.count < 20, !isReadOnly {
|
||||
let addButton = ScenicApplicationAddImageButton()
|
||||
addButton.addTarget(self, action: #selector(addTapped), for: .touchUpInside)
|
||||
stackView.addArrangedSubview(addButton)
|
||||
addButton.snp.makeConstraints { make in
|
||||
make.size.equalTo(80)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func addTapped() {
|
||||
onAdd?()
|
||||
}
|
||||
}
|
||||
|
||||
private final class ScenicApplicationAddImageButton: UIControl {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = AppColor.inputBackground
|
||||
layer.cornerRadius = 8
|
||||
|
||||
let icon = UIImageView(image: UIImage(systemName: "plus"))
|
||||
icon.tintColor = UIColor(hex: 0xB6BECA)
|
||||
let label = UILabel()
|
||||
label.text = "点击上传"
|
||||
label.font = .systemFont(ofSize: 12)
|
||||
label.textColor = UIColor(hex: 0xB6BECA)
|
||||
|
||||
let stack = UIStackView(arrangedSubviews: [icon, label])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 4
|
||||
stack.alignment = .center
|
||||
stack.isUserInteractionEnabled = false
|
||||
addSubview(stack)
|
||||
stack.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
private final class ScenicApplicationImageCell: UIView {
|
||||
|
||||
var onDelete: (() -> Void)?
|
||||
var onRetry: (() -> Void)?
|
||||
|
||||
private let imageView = UIImageView()
|
||||
private let progressView = UIProgressView(progressViewStyle: .default)
|
||||
private let deleteButton = UIButton(type: .system)
|
||||
private let retryButton = UIButton(type: .system)
|
||||
private let errorLabel = UILabel()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
layer.cornerRadius = 8
|
||||
clipsToBounds = true
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.clipsToBounds = true
|
||||
|
||||
deleteButton.setImage(UIImage(systemName: "xmark.circle.fill"), for: .normal)
|
||||
deleteButton.tintColor = .white
|
||||
deleteButton.addTarget(self, action: #selector(deleteTapped), for: .touchUpInside)
|
||||
|
||||
retryButton.setTitle("重试", for: .normal)
|
||||
retryButton.titleLabel?.font = .systemFont(ofSize: 12)
|
||||
retryButton.setTitleColor(.white, for: .normal)
|
||||
retryButton.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
retryButton.layer.cornerRadius = 4
|
||||
retryButton.addTarget(self, action: #selector(retryTapped), for: .touchUpInside)
|
||||
retryButton.isHidden = true
|
||||
|
||||
errorLabel.font = .systemFont(ofSize: 10)
|
||||
errorLabel.textColor = .white
|
||||
errorLabel.textAlignment = .center
|
||||
errorLabel.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
errorLabel.isHidden = true
|
||||
|
||||
addSubview(imageView)
|
||||
addSubview(progressView)
|
||||
addSubview(errorLabel)
|
||||
addSubview(retryButton)
|
||||
addSubview(deleteButton)
|
||||
|
||||
imageView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
progressView.snp.makeConstraints { make in
|
||||
make.leading.trailing.bottom.equalToSuperview().inset(4)
|
||||
}
|
||||
deleteButton.snp.makeConstraints { make in
|
||||
make.top.trailing.equalToSuperview().inset(2)
|
||||
make.size.equalTo(22)
|
||||
}
|
||||
retryButton.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
make.width.equalTo(44)
|
||||
make.height.equalTo(24)
|
||||
}
|
||||
errorLabel.snp.makeConstraints { make in
|
||||
make.leading.trailing.bottom.equalToSuperview()
|
||||
make.height.equalTo(18)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func apply(item: ScenicApplicationImageItem, showDelete: Bool) {
|
||||
deleteButton.isHidden = !showDelete
|
||||
progressView.isHidden = !item.isUploading
|
||||
progressView.progress = Float(item.uploadProgress) / 100
|
||||
|
||||
if let data = item.localData, let image = UIImage(data: data) {
|
||||
imageView.image = image
|
||||
} else if !item.remoteURL.isEmpty, let url = URL(string: item.remoteURL) {
|
||||
imageView.kf.setImage(with: url)
|
||||
} else {
|
||||
imageView.image = nil
|
||||
}
|
||||
|
||||
if let error = item.errorMessage, !error.isEmpty {
|
||||
errorLabel.isHidden = false
|
||||
errorLabel.text = "失败"
|
||||
retryButton.isHidden = false
|
||||
} else {
|
||||
errorLabel.isHidden = true
|
||||
retryButton.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func deleteTapped() {
|
||||
onDelete?()
|
||||
}
|
||||
|
||||
@objc private func retryTapped() {
|
||||
onRetry?()
|
||||
}
|
||||
}
|
||||
|
||||
/// 协议勾选行,对齐 Android `AgreementCheckbox`。
|
||||
final class ScenicApplicationAgreementView: UIView {
|
||||
|
||||
var onToggle: (() -> Void)?
|
||||
|
||||
private let checkbox = UIButton(type: .custom)
|
||||
private var isChecked = false
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
checkbox.setImage(UIImage(systemName: "square"), for: .normal)
|
||||
checkbox.setImage(UIImage(systemName: "checkmark.square.fill"), for: .selected)
|
||||
checkbox.tintColor = AppColor.primaryBanner
|
||||
checkbox.addTarget(self, action: #selector(toggleTapped), for: .touchUpInside)
|
||||
|
||||
let prefix = UILabel()
|
||||
prefix.text = "已阅读并同意"
|
||||
prefix.font = .systemFont(ofSize: 14)
|
||||
prefix.textColor = AppColor.textSecondary
|
||||
|
||||
let userNotice = UILabel()
|
||||
userNotice.text = "《用户须知》"
|
||||
userNotice.font = .systemFont(ofSize: 14)
|
||||
userNotice.textColor = AppColor.primaryBanner
|
||||
|
||||
let middle = UILabel()
|
||||
middle.text = "与"
|
||||
middle.font = .systemFont(ofSize: 14)
|
||||
middle.textColor = AppColor.textSecondary
|
||||
|
||||
let privacy = UILabel()
|
||||
privacy.text = "《隐私政策》"
|
||||
privacy.font = .systemFont(ofSize: 14)
|
||||
privacy.textColor = AppColor.primaryBanner
|
||||
|
||||
let textStack = UIStackView(arrangedSubviews: [prefix, userNotice, middle, privacy])
|
||||
textStack.axis = .horizontal
|
||||
textStack.spacing = 2
|
||||
textStack.alignment = .center
|
||||
|
||||
let row = UIStackView(arrangedSubviews: [checkbox, textStack])
|
||||
row.axis = .horizontal
|
||||
row.spacing = 2
|
||||
row.alignment = .center
|
||||
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(toggleTapped))
|
||||
addGestureRecognizer(tap)
|
||||
|
||||
addSubview(row)
|
||||
row.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
checkbox.snp.makeConstraints { make in
|
||||
make.size.equalTo(24)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setChecked(_ checked: Bool) {
|
||||
isChecked = checked
|
||||
checkbox.isSelected = checked
|
||||
}
|
||||
|
||||
@objc private func toggleTapped() {
|
||||
onToggle?()
|
||||
}
|
||||
}
|
||||
|
||||
/// 底部确认提交按钮。
|
||||
final class ScenicApplicationSubmitButton: UIButton {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setTitle("确认提交", for: .normal)
|
||||
setTitleColor(.white, for: .normal)
|
||||
titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
backgroundColor = AppColor.primaryBanner
|
||||
layer.cornerRadius = 8
|
||||
snp.makeConstraints { make in
|
||||
make.height.equalTo(46)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func apply(canSubmit: Bool, isSubmitting: Bool, isReadOnly: Bool) {
|
||||
let enabled = !isReadOnly && canSubmit && !isSubmitting
|
||||
isEnabled = enabled
|
||||
alpha = enabled ? 1 : 1
|
||||
backgroundColor = enabled ? AppColor.primaryBanner : UIColor(hex: 0xE0E0E0)
|
||||
setTitle(isSubmitting ? "提交中..." : "确认提交", for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
/// 上传进度弹窗。
|
||||
final class ScenicApplicationUploadProgressView: UIView {
|
||||
|
||||
private let titleLabel = UILabel()
|
||||
private let progressView = UIProgressView(progressViewStyle: .default)
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = UIColor.black.withAlphaComponent(0.35)
|
||||
|
||||
let card = UIView()
|
||||
card.backgroundColor = .white
|
||||
card.layer.cornerRadius = 12
|
||||
|
||||
titleLabel.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
titleLabel.textColor = AppColor.textPrimary
|
||||
titleLabel.textAlignment = .center
|
||||
|
||||
progressView.progressTintColor = AppColor.primaryBanner
|
||||
|
||||
let stack = UIStackView(arrangedSubviews: [titleLabel, progressView])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 16
|
||||
card.addSubview(stack)
|
||||
stack.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(20)
|
||||
}
|
||||
|
||||
addSubview(card)
|
||||
card.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
make.leading.trailing.equalToSuperview().inset(40)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func apply(state: ScenicApplicationUploadDialogState?) {
|
||||
isHidden = state == nil
|
||||
guard let state else { return }
|
||||
titleLabel.text = state.title
|
||||
progressView.progress = Float(state.progress) / 100
|
||||
}
|
||||
}
|
||||
|
||||
/// 带 placeholder 的多行输入框。
|
||||
final class ScenicApplicationPlaceholderTextView: UITextView, UITextViewDelegate {
|
||||
|
||||
private let placeholderLabel = UILabel()
|
||||
|
||||
var placeholder: String {
|
||||
get { placeholderLabel.text ?? "" }
|
||||
set {
|
||||
placeholderLabel.text = newValue
|
||||
refreshPlaceholderVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
var onTextChange: ((String) -> Void)?
|
||||
|
||||
override var text: String! {
|
||||
didSet { refreshPlaceholderVisibility() }
|
||||
}
|
||||
|
||||
override var delegate: UITextViewDelegate? {
|
||||
get { super.delegate }
|
||||
set {
|
||||
// 始终由自身处理 placeholder,外部 delegate 通过 onTextChange 接收变更。
|
||||
}
|
||||
}
|
||||
|
||||
init(placeholder: String) {
|
||||
super.init(frame: .zero, textContainer: nil)
|
||||
font = .systemFont(ofSize: 14)
|
||||
textColor = AppColor.textPrimary
|
||||
backgroundColor = .white
|
||||
layer.cornerRadius = 8
|
||||
layer.borderWidth = 1
|
||||
layer.borderColor = AppColor.border.cgColor
|
||||
textContainerInset = UIEdgeInsets(top: 10, left: 8, bottom: 10, right: 8)
|
||||
super.delegate = self
|
||||
|
||||
placeholderLabel.text = placeholder
|
||||
placeholderLabel.font = font
|
||||
placeholderLabel.textColor = AppColor.textTertiary
|
||||
placeholderLabel.numberOfLines = 0
|
||||
addSubview(placeholderLabel)
|
||||
placeholderLabel.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(10)
|
||||
make.leading.equalToSuperview().offset(12)
|
||||
make.trailing.equalToSuperview().offset(-12)
|
||||
}
|
||||
snp.makeConstraints { make in
|
||||
make.height.equalTo(100)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func textViewDidChange(_ textView: UITextView) {
|
||||
refreshPlaceholderVisibility()
|
||||
onTextChange?(textView.text ?? "")
|
||||
}
|
||||
|
||||
private func refreshPlaceholderVisibility() {
|
||||
placeholderLabel.isHidden = !(text ?? "").isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
/// 带边框的输入框样式。
|
||||
enum ScenicApplicationFieldStyle {
|
||||
static func makeTextField(placeholder: String) -> UITextField {
|
||||
let field = UITextField()
|
||||
field.placeholder = placeholder
|
||||
field.font = .systemFont(ofSize: 14)
|
||||
field.borderStyle = .none
|
||||
field.backgroundColor = .white
|
||||
field.layer.cornerRadius = 8
|
||||
field.layer.borderWidth = 1
|
||||
field.layer.borderColor = AppColor.border.cgColor
|
||||
field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 1))
|
||||
field.leftViewMode = .always
|
||||
field.snp.makeConstraints { make in
|
||||
make.height.equalTo(44)
|
||||
}
|
||||
return field
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user