对齐 Android 无权限强制弹窗、角色下拉与申请页交互,补充 UIButton configuration 适配,并增强景区排队 TTS 与相关单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
591 lines
23 KiB
Swift
591 lines
23 KiB
Swift
//
|
|
// PermissionRoleViews.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import SnapKit
|
|
import UIKit
|
|
|
|
/// “已有角色信息”中的稳定可展开条目。
|
|
private struct PermissionExistingRoleItem: Hashable {
|
|
let id: String
|
|
let roleName: String
|
|
let scenics: [ScenicInfo]
|
|
var isExpanded: Bool
|
|
}
|
|
|
|
/// Android 风格“已有角色信息”折叠列表。
|
|
final class PermissionExistingRoleListView: UIView, UITableViewDelegate {
|
|
|
|
private enum Section { case main }
|
|
|
|
var onShowAllScenics: ((String, [ScenicInfo]) -> Void)?
|
|
|
|
private let titleLabel = UILabel()
|
|
private let tableView = UITableView(frame: .zero, style: .plain)
|
|
private var dataSource: UITableViewDiffableDataSource<Section, String>!
|
|
private var items: [PermissionExistingRoleItem] = []
|
|
private var tableHeightConstraint: Constraint?
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
backgroundColor = PermissionApplyUITokens.existingBackground
|
|
layer.cornerRadius = 12
|
|
titleLabel.text = "已有角色信息"
|
|
titleLabel.font = .systemFont(ofSize: 16, weight: .medium)
|
|
titleLabel.textColor = PermissionApplyUITokens.text
|
|
|
|
tableView.backgroundColor = .clear
|
|
tableView.separatorStyle = .none
|
|
tableView.isScrollEnabled = false
|
|
tableView.rowHeight = UITableView.automaticDimension
|
|
tableView.estimatedRowHeight = 52
|
|
tableView.delegate = self
|
|
tableView.register(PermissionExistingRoleCell.self, forCellReuseIdentifier: PermissionExistingRoleCell.reuseIdentifier)
|
|
|
|
addSubview(titleLabel)
|
|
addSubview(tableView)
|
|
titleLabel.snp.makeConstraints { make in
|
|
make.top.leading.trailing.equalToSuperview().inset(12)
|
|
}
|
|
tableView.snp.makeConstraints { make in
|
|
make.top.equalTo(titleLabel.snp.bottom).offset(12)
|
|
make.leading.trailing.bottom.equalToSuperview().inset(12)
|
|
tableHeightConstraint = make.height.equalTo(1).constraint
|
|
}
|
|
|
|
dataSource = UITableViewDiffableDataSource<Section, String>(tableView: tableView) {
|
|
[weak self] tableView, indexPath, itemID in
|
|
guard let self,
|
|
let item = self.items.first(where: { $0.id == itemID }),
|
|
let cell = tableView.dequeueReusableCell(
|
|
withIdentifier: PermissionExistingRoleCell.reuseIdentifier,
|
|
for: indexPath
|
|
) as? PermissionExistingRoleCell else { return UITableViewCell() }
|
|
cell.apply(item: item)
|
|
cell.onToggle = { [weak self] in self?.toggle(itemID: item.id) }
|
|
cell.onShowAll = { [weak self] in self?.onShowAllScenics?(item.roleName, item.scenics) }
|
|
return cell
|
|
}
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
/// 使用本地权限缓存构建已有角色列表。
|
|
func apply(rolePermissions: [RolePermissionResponse]) {
|
|
let expandedIDs = Set(items.filter(\.isExpanded).map(\.id))
|
|
items = rolePermissions.enumerated().map { index, value in
|
|
let id = "\(index)-\(value.role.id)-\(value.role.name)"
|
|
return PermissionExistingRoleItem(
|
|
id: id,
|
|
roleName: value.role.name,
|
|
scenics: value.scenic,
|
|
isExpanded: expandedIDs.contains(id)
|
|
)
|
|
}
|
|
isHidden = items.isEmpty
|
|
applySnapshot(animatingExpansion: false, reconfigureExisting: true)
|
|
}
|
|
|
|
private func toggle(itemID: String) {
|
|
guard let index = items.firstIndex(where: { $0.id == itemID }) else { return }
|
|
items[index].isExpanded.toggle()
|
|
if let indexPath = dataSource.indexPath(for: itemID),
|
|
let cell = tableView.cellForRow(at: indexPath) as? PermissionExistingRoleCell {
|
|
cell.apply(item: items[index], animated: true)
|
|
}
|
|
applySnapshot(animatingExpansion: true, reconfigureExisting: false)
|
|
}
|
|
|
|
private func applySnapshot(animatingExpansion: Bool, reconfigureExisting: Bool) {
|
|
var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
|
|
snapshot.appendSections([.main])
|
|
let itemIDs = items.map(\.id)
|
|
snapshot.appendItems(itemIDs)
|
|
if reconfigureExisting {
|
|
let currentIDs = Set(dataSource.snapshot().itemIdentifiers)
|
|
snapshot.reconfigureItems(itemIDs.filter(currentIDs.contains))
|
|
}
|
|
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
|
self?.updateHeight(animated: animatingExpansion)
|
|
}
|
|
}
|
|
|
|
private func updateHeight(animated: Bool) {
|
|
let layoutRoot = superview ?? self
|
|
layoutRoot.layoutIfNeeded()
|
|
let updates = { [self] in
|
|
tableView.beginUpdates()
|
|
tableView.endUpdates()
|
|
tableView.layoutIfNeeded()
|
|
tableHeightConstraint?.update(offset: max(1, tableView.contentSize.height))
|
|
invalidateIntrinsicContentSize()
|
|
layoutRoot.layoutIfNeeded()
|
|
}
|
|
guard animated, !UIAccessibility.isReduceMotionEnabled else {
|
|
UIView.performWithoutAnimation(updates)
|
|
return
|
|
}
|
|
UIView.animate(
|
|
withDuration: 0.24,
|
|
delay: 0,
|
|
options: [.curveEaseOut, .beginFromCurrentState, .allowUserInteraction],
|
|
animations: updates
|
|
)
|
|
}
|
|
}
|
|
|
|
/// 已有角色折叠列表单元格。
|
|
private final class PermissionExistingRoleCell: UITableViewCell {
|
|
|
|
static let reuseIdentifier = "PermissionExistingRoleCell"
|
|
|
|
var onToggle: (() -> Void)?
|
|
var onShowAll: (() -> Void)?
|
|
|
|
private let cardView = UIView()
|
|
private let headerControl = UIControl()
|
|
private let titleLabel = UILabel()
|
|
private let chevron = UIImageView()
|
|
private let expandedContainer = UIView()
|
|
private let flowView = PermissionSimpleFlowView()
|
|
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
backgroundColor = .clear
|
|
selectionStyle = .none
|
|
contentView.backgroundColor = .clear
|
|
cardView.backgroundColor = .white
|
|
cardView.layer.cornerRadius = 8
|
|
cardView.layer.shadowColor = UIColor.black.cgColor
|
|
cardView.layer.shadowOpacity = 0.08
|
|
cardView.layer.shadowRadius = 1
|
|
cardView.layer.shadowOffset = CGSize(width: 0, height: 1)
|
|
|
|
titleLabel.font = .systemFont(ofSize: 14, weight: .medium)
|
|
titleLabel.textColor = PermissionApplyUITokens.text
|
|
chevron.image = UIImage(systemName: "chevron.down")
|
|
chevron.tintColor = AppColor.textTertiary
|
|
chevron.contentMode = .scaleAspectFit
|
|
headerControl.addTarget(self, action: #selector(toggleTapped), for: .touchUpInside)
|
|
expandedContainer.backgroundColor = PermissionApplyUITokens.existingBackground
|
|
flowView.onMore = { [weak self] in self?.onShowAll?() }
|
|
|
|
contentView.addSubview(cardView)
|
|
cardView.addSubview(headerControl)
|
|
headerControl.addSubview(titleLabel)
|
|
headerControl.addSubview(chevron)
|
|
cardView.addSubview(expandedContainer)
|
|
expandedContainer.addSubview(flowView)
|
|
|
|
cardView.snp.makeConstraints { make in
|
|
make.top.equalToSuperview()
|
|
make.leading.trailing.equalToSuperview()
|
|
make.bottom.equalToSuperview().offset(-8)
|
|
}
|
|
headerControl.snp.makeConstraints { make in
|
|
make.top.leading.trailing.equalToSuperview()
|
|
make.height.equalTo(44)
|
|
}
|
|
titleLabel.snp.makeConstraints { make in
|
|
make.leading.equalToSuperview().offset(16)
|
|
make.centerY.equalToSuperview()
|
|
make.trailing.lessThanOrEqualTo(chevron.snp.leading).offset(-8)
|
|
}
|
|
chevron.snp.makeConstraints { make in
|
|
make.trailing.equalToSuperview().offset(-16)
|
|
make.centerY.equalToSuperview()
|
|
make.size.equalTo(20)
|
|
}
|
|
expandedContainer.snp.makeConstraints { make in
|
|
make.top.equalTo(headerControl.snp.bottom)
|
|
make.leading.trailing.bottom.equalToSuperview()
|
|
}
|
|
flowView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(12) }
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func apply(item: PermissionExistingRoleItem, animated: Bool = false) {
|
|
titleLabel.text = item.roleName
|
|
headerControl.accessibilityLabel = item.roleName
|
|
headerControl.accessibilityValue = item.isExpanded ? "已展开" : "已收起"
|
|
flowView.apply(titles: item.scenics.map(\.name), showsMore: item.scenics.count > 3)
|
|
let showsExpandedContent = item.isExpanded && !item.scenics.isEmpty
|
|
let isExpanding = expandedContainer.isHidden && showsExpandedContent
|
|
expandedContainer.isHidden = !showsExpandedContent
|
|
|
|
let updates = { [self] in
|
|
chevron.transform = item.isExpanded
|
|
? CGAffineTransform(rotationAngle: .pi)
|
|
: .identity
|
|
expandedContainer.alpha = 1
|
|
expandedContainer.transform = .identity
|
|
}
|
|
guard animated, !UIAccessibility.isReduceMotionEnabled else {
|
|
UIView.performWithoutAnimation(updates)
|
|
return
|
|
}
|
|
if isExpanding {
|
|
expandedContainer.alpha = 0
|
|
expandedContainer.transform = CGAffineTransform(translationX: 0, y: -8)
|
|
}
|
|
UIView.animate(
|
|
withDuration: 0.24,
|
|
delay: 0,
|
|
options: [.curveEaseOut, .beginFromCurrentState, .allowUserInteraction],
|
|
animations: updates
|
|
)
|
|
}
|
|
|
|
@objc private func toggleTapped() {
|
|
onToggle?()
|
|
}
|
|
}
|
|
|
|
/// 小规模绿色景区标签的手动换行容器。
|
|
private final class PermissionSimpleFlowView: UIView {
|
|
|
|
var onMore: (() -> Void)?
|
|
|
|
private var itemViews: [UIView] = []
|
|
private var lastLayoutWidth: CGFloat = 0
|
|
|
|
func apply(titles: [String], showsMore: Bool) {
|
|
itemViews.forEach { $0.removeFromSuperview() }
|
|
itemViews = []
|
|
var values = Array(titles.prefix(3))
|
|
if showsMore { values.append("···") }
|
|
for value in values {
|
|
let button = UIButton(type: .system)
|
|
button.setTitle(value, for: .normal)
|
|
button.setTitleColor(PermissionApplyUITokens.existingGreen, for: .normal)
|
|
button.titleLabel?.font = .systemFont(ofSize: 14)
|
|
button.setConfigurationContentInsets(
|
|
NSDirectionalEdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12)
|
|
)
|
|
button.backgroundColor = PermissionApplyUITokens.existingGreenBackground
|
|
button.layer.cornerRadius = 4
|
|
button.layer.borderWidth = 1
|
|
button.layer.borderColor = PermissionApplyUITokens.existingGreen.cgColor
|
|
button.isUserInteractionEnabled = value == "···"
|
|
if value == "···" {
|
|
button.addTarget(self, action: #selector(moreTapped), for: .touchUpInside)
|
|
button.accessibilityLabel = "查看全部景区"
|
|
}
|
|
addSubview(button)
|
|
itemViews.append(button)
|
|
}
|
|
setNeedsLayout()
|
|
invalidateIntrinsicContentSize()
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
guard bounds.width > 0 else { return }
|
|
let widthChanged = abs(bounds.width - lastLayoutWidth) > 0.5
|
|
lastLayoutWidth = bounds.width
|
|
var x: CGFloat = 0
|
|
var y: CGFloat = 0
|
|
var rowHeight: CGFloat = 0
|
|
for view in itemViews {
|
|
let fitted = view.sizeThatFits(CGSize(width: min(180, bounds.width), height: 34))
|
|
let width = min(bounds.width, max(42, fitted.width))
|
|
let height = max(34, fitted.height)
|
|
if x > 0, x + width > bounds.width {
|
|
x = 0
|
|
y += rowHeight + 12
|
|
rowHeight = 0
|
|
}
|
|
view.frame = CGRect(x: x, y: y, width: width, height: height)
|
|
x += width + 12
|
|
rowHeight = max(rowHeight, height)
|
|
}
|
|
if widthChanged { invalidateIntrinsicContentSize() }
|
|
}
|
|
|
|
override var intrinsicContentSize: CGSize {
|
|
let width = bounds.width > 0 ? bounds.width : 280
|
|
var x: CGFloat = 0
|
|
var totalHeight: CGFloat = 0
|
|
var rowHeight: CGFloat = 0
|
|
for view in itemViews {
|
|
let fitted = view.sizeThatFits(CGSize(width: min(180, width), height: 34))
|
|
let itemWidth = min(width, max(42, fitted.width))
|
|
let itemHeight = max(34, fitted.height)
|
|
if x > 0, x + itemWidth > width {
|
|
totalHeight += rowHeight + 12
|
|
x = 0
|
|
rowHeight = 0
|
|
}
|
|
x += itemWidth + 12
|
|
rowHeight = max(rowHeight, itemHeight)
|
|
}
|
|
return CGSize(width: UIView.noIntrinsicMetric, height: itemViews.isEmpty ? 0 : totalHeight + rowHeight)
|
|
}
|
|
|
|
@objc private func moreTapped() {
|
|
onMore?()
|
|
}
|
|
}
|
|
|
|
/// 审核页“新增角色/新增景区”白色卡片。
|
|
final class PermissionStaticSectionView: UIView {
|
|
|
|
private let titleLabel = UILabel()
|
|
private let chips = PermissionChipCollectionView(style: .selectedStatic)
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
backgroundColor = .white
|
|
layer.cornerRadius = 12
|
|
titleLabel.font = .systemFont(ofSize: 16, weight: .medium)
|
|
titleLabel.textColor = PermissionApplyUITokens.text
|
|
addSubview(titleLabel)
|
|
addSubview(chips)
|
|
titleLabel.snp.makeConstraints { make in
|
|
make.top.leading.trailing.equalToSuperview().inset(16)
|
|
}
|
|
chips.snp.makeConstraints { make in
|
|
make.top.equalTo(titleLabel.snp.bottom).offset(12)
|
|
make.leading.trailing.bottom.equalToSuperview().inset(16)
|
|
}
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
/// 更新标题和不可删除的蓝色标签。
|
|
func apply(title: String, names: [String]) {
|
|
titleLabel.text = title
|
|
chips.apply(titles: names)
|
|
}
|
|
}
|
|
|
|
/// Android 风格角色或景区删除确认弹窗。
|
|
final class PermissionDeleteDialogViewController: UIViewController {
|
|
|
|
private let target: PermissionDeletionTarget
|
|
private let onCancel: () -> Void
|
|
private let onConfirm: () -> Void
|
|
|
|
/// 创建删除确认弹窗。
|
|
init(target: PermissionDeletionTarget, onCancel: @escaping () -> Void, onConfirm: @escaping () -> Void) {
|
|
self.target = target
|
|
self.onCancel = onCancel
|
|
self.onConfirm = onConfirm
|
|
super.init(nibName: nil, bundle: nil)
|
|
modalPresentationStyle = .overFullScreen
|
|
modalTransitionStyle = .crossDissolve
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
view.backgroundColor = UIColor.black.withAlphaComponent(0.32)
|
|
let card = UIView()
|
|
card.backgroundColor = .white
|
|
card.layer.cornerRadius = 16
|
|
|
|
let titleLabel = UILabel()
|
|
titleLabel.text = target.dialogTitle
|
|
titleLabel.font = .systemFont(ofSize: 18, weight: .bold)
|
|
titleLabel.textColor = UIColor(hex: 0xFF0000)
|
|
|
|
let messageLabel = UILabel()
|
|
messageLabel.numberOfLines = 0
|
|
let message = NSMutableAttributedString(
|
|
string: target.dialogMessage,
|
|
attributes: [.font: UIFont.systemFont(ofSize: 16), .foregroundColor: UIColor(hex: 0x333333)]
|
|
)
|
|
let name: String
|
|
switch target {
|
|
case .role(let value), .scenic(_, let value): name = value
|
|
}
|
|
let range = (target.dialogMessage as NSString).range(of: name)
|
|
if range.location != NSNotFound {
|
|
message.addAttribute(.font, value: UIFont.systemFont(ofSize: 16, weight: .bold), range: range)
|
|
}
|
|
messageLabel.attributedText = message
|
|
|
|
let cancel = makeButton(title: "取消", background: UIColor(hex: 0xFFE5E5))
|
|
let confirm = makeButton(title: "确认", background: UIColor(hex: 0xFF0000))
|
|
cancel.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
|
|
confirm.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside)
|
|
let buttons = UIStackView(arrangedSubviews: [cancel, confirm])
|
|
buttons.axis = .horizontal
|
|
buttons.spacing = 12
|
|
buttons.distribution = .fillEqually
|
|
buttons.snp.makeConstraints { make in make.height.equalTo(44) }
|
|
|
|
let stack = UIStackView(arrangedSubviews: [titleLabel, messageLabel, buttons])
|
|
stack.axis = .vertical
|
|
stack.spacing = 16
|
|
stack.setCustomSpacing(24, after: messageLabel)
|
|
view.addSubview(card)
|
|
card.addSubview(stack)
|
|
card.snp.makeConstraints { make in
|
|
make.center.equalToSuperview()
|
|
make.leading.trailing.equalToSuperview().inset(32)
|
|
}
|
|
stack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(24) }
|
|
}
|
|
|
|
private func makeButton(title: String, background: UIColor) -> UIButton {
|
|
let button = UIButton(type: .system)
|
|
button.setTitle(title, for: .normal)
|
|
button.setTitleColor(.white, for: .normal)
|
|
button.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
|
button.backgroundColor = background
|
|
button.layer.cornerRadius = 8
|
|
button.accessibilityLabel = title
|
|
return button
|
|
}
|
|
|
|
@objc private func cancelTapped() {
|
|
dismiss(animated: true) { [onCancel] in onCancel() }
|
|
}
|
|
|
|
@objc private func confirmTapped() {
|
|
dismiss(animated: true) { [onConfirm] in onConfirm() }
|
|
}
|
|
}
|
|
|
|
/// 某个已有角色的全部景区弹窗。
|
|
final class PermissionScenicListDialogViewController: UIViewController {
|
|
|
|
private enum Section { case main }
|
|
|
|
private let roleName: String
|
|
private let scenics: [ScenicInfo]
|
|
private let onDismiss: () -> Void
|
|
private let tableView = UITableView(frame: .zero, style: .plain)
|
|
private var dataSource: UITableViewDiffableDataSource<Section, ScenicInfo>!
|
|
|
|
/// 创建固定400pt列表高度的景区详情弹窗。
|
|
init(roleName: String, scenics: [ScenicInfo], onDismiss: @escaping () -> Void) {
|
|
self.roleName = roleName
|
|
self.scenics = scenics
|
|
self.onDismiss = onDismiss
|
|
super.init(nibName: nil, bundle: nil)
|
|
modalPresentationStyle = .overFullScreen
|
|
modalTransitionStyle = .crossDissolve
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
view.backgroundColor = UIColor.black.withAlphaComponent(0.32)
|
|
let card = UIView()
|
|
card.backgroundColor = .white
|
|
card.layer.cornerRadius = 16
|
|
|
|
let titleLabel = UILabel()
|
|
titleLabel.text = roleName
|
|
titleLabel.font = .systemFont(ofSize: 18, weight: .bold)
|
|
titleLabel.textColor = UIColor(hex: 0x333333)
|
|
let closeButton = UIButton(type: .system)
|
|
closeButton.setImage(UIImage(systemName: "xmark"), for: .normal)
|
|
closeButton.tintColor = AppColor.textSecondary
|
|
closeButton.accessibilityLabel = "关闭"
|
|
closeButton.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
|
|
|
|
tableView.separatorStyle = .none
|
|
tableView.backgroundColor = .white
|
|
tableView.rowHeight = 50
|
|
tableView.register(PermissionScenicDialogCell.self, forCellReuseIdentifier: PermissionScenicDialogCell.reuseIdentifier)
|
|
dataSource = UITableViewDiffableDataSource<Section, ScenicInfo>(tableView: tableView) {
|
|
tableView, indexPath, scenic in
|
|
guard let cell = tableView.dequeueReusableCell(
|
|
withIdentifier: PermissionScenicDialogCell.reuseIdentifier,
|
|
for: indexPath
|
|
) as? PermissionScenicDialogCell else { return UITableViewCell() }
|
|
cell.apply(name: scenic.name)
|
|
return cell
|
|
}
|
|
|
|
view.addSubview(card)
|
|
[titleLabel, closeButton, tableView].forEach(card.addSubview)
|
|
card.snp.makeConstraints { make in
|
|
make.center.equalToSuperview()
|
|
make.leading.trailing.equalToSuperview().inset(32)
|
|
}
|
|
titleLabel.snp.makeConstraints { make in
|
|
make.top.leading.equalToSuperview().inset(24)
|
|
make.trailing.lessThanOrEqualTo(closeButton.snp.leading).offset(-8)
|
|
make.centerY.equalTo(closeButton)
|
|
}
|
|
closeButton.snp.makeConstraints { make in
|
|
make.top.trailing.equalToSuperview().inset(16)
|
|
make.size.equalTo(44)
|
|
}
|
|
tableView.snp.makeConstraints { make in
|
|
make.top.equalTo(closeButton.snp.bottom).offset(8)
|
|
make.leading.trailing.bottom.equalToSuperview().inset(24)
|
|
make.height.equalTo(400)
|
|
}
|
|
|
|
var snapshot = NSDiffableDataSourceSnapshot<Section, ScenicInfo>()
|
|
snapshot.appendSections([.main])
|
|
snapshot.appendItems(scenics)
|
|
dataSource.apply(snapshot, animatingDifferences: false)
|
|
}
|
|
|
|
@objc private func closeTapped() {
|
|
dismiss(animated: true) { [onDismiss] in onDismiss() }
|
|
}
|
|
}
|
|
|
|
/// 景区详情弹窗中的绿色标签行。
|
|
private final class PermissionScenicDialogCell: UITableViewCell {
|
|
|
|
static let reuseIdentifier = "PermissionScenicDialogCell"
|
|
private let chipLabel = UILabel()
|
|
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
selectionStyle = .none
|
|
chipLabel.font = .systemFont(ofSize: 14)
|
|
chipLabel.textColor = PermissionApplyUITokens.existingGreen
|
|
chipLabel.backgroundColor = PermissionApplyUITokens.existingGreenBackground
|
|
chipLabel.layer.cornerRadius = 4
|
|
chipLabel.layer.borderWidth = 1
|
|
chipLabel.layer.borderColor = PermissionApplyUITokens.existingGreen.cgColor
|
|
chipLabel.clipsToBounds = true
|
|
chipLabel.textAlignment = .center
|
|
contentView.addSubview(chipLabel)
|
|
chipLabel.snp.makeConstraints { make in
|
|
make.leading.equalToSuperview()
|
|
make.centerY.equalToSuperview()
|
|
make.height.equalTo(34)
|
|
make.trailing.lessThanOrEqualToSuperview()
|
|
}
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func apply(name: String) {
|
|
chipLabel.text = " \(name) "
|
|
chipLabel.accessibilityLabel = name
|
|
}
|
|
}
|