// // ScenicSelectionViews.swift // suixinkan // import Kingfisher import SnapKit import UIKit /// 景区选择页橙色权限申请横幅。 final class ScenicPermissionBannerView: UIView { var onApplyNow: (() -> Void)? private let titleLabel = UILabel() private let applyButton = UIButton(type: .system) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = AppColor.warningBackground titleLabel.text = "希望开通更多景区权限" titleLabel.font = .systemFont(ofSize: 14, weight: .medium) titleLabel.textColor = AppColor.warning titleLabel.numberOfLines = 2 applyButton.setTitle("立即申请 ›", for: .normal) applyButton.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium) applyButton.setTitleColor(AppColor.warning, for: .normal) applyButton.addTarget(self, action: #selector(applyTapped), for: .touchUpInside) addSubview(titleLabel) addSubview(applyButton) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().offset(16) make.centerY.equalToSuperview() make.trailing.lessThanOrEqualTo(applyButton.snp.leading).offset(-8) } applyButton.snp.makeConstraints { make in make.trailing.equalToSuperview().offset(-16) make.centerY.equalToSuperview() } snp.makeConstraints { make in make.height.equalTo(44) } setContentHuggingPriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .vertical) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func applyTapped() { onApplyNow?() } } /// 景区搜索栏。 final class ScenicSelectionSearchBar: UIView, UITextFieldDelegate { var onSearchTextChange: ((String) -> Void)? let textField = UITextField() override init(frame: CGRect) { super.init(frame: frame) backgroundColor = UIColor(hex: 0xF2F4F8) layer.cornerRadius = 12 clipsToBounds = true let icon = UIImageView(image: UIImage(systemName: "magnifyingglass")) icon.tintColor = UIColor(hex: 0xB3B8C2) icon.contentMode = .scaleAspectFit textField.placeholder = "搜索景区" textField.font = .systemFont(ofSize: 14) textField.textColor = AppColor.textPrimary textField.returnKeyType = .search textField.delegate = self textField.addTarget(self, action: #selector(textChanged), for: .editingChanged) addSubview(icon) addSubview(textField) icon.snp.makeConstraints { make in make.leading.equalToSuperview().offset(12) make.centerY.equalToSuperview() make.size.equalTo(18) } textField.snp.makeConstraints { make in make.leading.equalTo(icon.snp.trailing).offset(8) make.trailing.equalToSuperview().offset(-12) make.top.bottom.equalToSuperview().inset(10) } snp.makeConstraints { make in make.height.equalTo(44) } } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func textChanged() { onSearchTextChange?(textField.text ?? "") } func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } } /// 当前定位栏。 final class ScenicCurrentLocationBar: UIView { var onRelocate: (() -> Void)? private let iconView = UIImageView(image: UIImage(systemName: "location.fill")) private let locationLabel = UILabel() private let relocateButton = UIButton(type: .system) override init(frame: CGRect) { super.init(frame: frame) iconView.tintColor = AppColor.primary iconView.contentMode = .scaleAspectFit locationLabel.font = .systemFont(ofSize: 14) locationLabel.textColor = AppColor.textPrimary locationLabel.numberOfLines = 2 relocateButton.setTitle("重新定位", for: .normal) relocateButton.titleLabel?.font = .systemFont(ofSize: 12) relocateButton.setTitleColor(AppColor.primary, for: .normal) relocateButton.backgroundColor = AppColor.primaryLight relocateButton.layer.cornerRadius = 4 relocateButton.contentEdgeInsets = UIEdgeInsets(top: 2, left: 8, bottom: 2, right: 8) relocateButton.addTarget(self, action: #selector(relocateTapped), for: .touchUpInside) relocateButton.setContentHuggingPriority(.required, for: .horizontal) relocateButton.setContentCompressionResistancePriority(.required, for: .horizontal) addSubview(iconView) addSubview(locationLabel) addSubview(relocateButton) iconView.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalTo(locationLabel) make.size.equalTo(16) } locationLabel.snp.makeConstraints { make in make.leading.equalTo(iconView.snp.trailing).offset(4) make.top.bottom.equalToSuperview().inset(8) make.trailing.lessThanOrEqualTo(relocateButton.snp.leading).offset(-12) } relocateButton.snp.makeConstraints { make in make.trailing.equalToSuperview() make.centerY.equalTo(locationLabel) } setContentHuggingPriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .vertical) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func apply(locationText: String, isLocating: Bool = false) { locationLabel.text = isLocating ? "定位中..." : locationText relocateButton.isEnabled = !isLocating relocateButton.alpha = isLocating ? 0.5 : 1 } @objc private func relocateTapped() { onRelocate?() } } /// 景区卡片 Cell。 final class ScenicSpotCell: UITableViewCell { static let reuseIdentifier = "ScenicSpotCell" var onTap: (() -> Void)? private let cardView = UIView() private let coverImageView = UIImageView() private let nameLabel = UILabel() private let distanceLabel = UILabel() private let addressLabel = UILabel() private let statusTag = UILabel() private let selectedBorderColor = UIColor(hex: 0xE74C3C) override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = .clear contentView.backgroundColor = .clear cardView.backgroundColor = AppColor.pageBackgroundSoft cardView.layer.cornerRadius = 8 cardView.layer.borderWidth = 2 cardView.layer.borderColor = UIColor(hex: 0xF4F4F4).cgColor coverImageView.contentMode = .scaleAspectFill coverImageView.clipsToBounds = true coverImageView.layer.cornerRadius = 6 coverImageView.backgroundColor = UIColor(hex: 0xF0F0F0) nameLabel.font = .systemFont(ofSize: 16, weight: .bold) nameLabel.textColor = AppColor.textPrimary distanceLabel.font = .systemFont(ofSize: 12) distanceLabel.textColor = AppColor.textSecondary addressLabel.font = .systemFont(ofSize: 12) addressLabel.textColor = AppColor.textTertiary addressLabel.numberOfLines = 2 statusTag.font = .systemFont(ofSize: 11, weight: .medium) statusTag.textColor = .white statusTag.textAlignment = .center statusTag.layer.cornerRadius = 4 statusTag.clipsToBounds = true statusTag.isHidden = true contentView.addSubview(cardView) cardView.addSubview(coverImageView) cardView.addSubview(nameLabel) cardView.addSubview(statusTag) cardView.addSubview(distanceLabel) cardView.addSubview(addressLabel) cardView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 12, right: 16)) } coverImageView.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview().inset(12) make.size.equalTo(80) } nameLabel.snp.makeConstraints { make in make.leading.equalTo(coverImageView.snp.trailing).offset(12) make.top.equalToSuperview().offset(12) make.trailing.lessThanOrEqualTo(statusTag.snp.leading).offset(-8) } statusTag.snp.makeConstraints { make in make.trailing.equalToSuperview().offset(-12) make.centerY.equalTo(nameLabel) make.height.equalTo(20) } distanceLabel.snp.makeConstraints { make in make.leading.equalTo(nameLabel) make.top.equalTo(nameLabel.snp.bottom).offset(6) } addressLabel.snp.makeConstraints { make in make.leading.equalTo(nameLabel) make.trailing.equalToSuperview().offset(-12) make.top.equalTo(distanceLabel.snp.bottom).offset(4) make.bottom.lessThanOrEqualToSuperview().offset(-12) } let tap = UITapGestureRecognizer(target: self, action: #selector(cardTapped)) cardView.addGestureRecognizer(tap) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func apply(_ item: ScenicSpotDisplayItem) { nameLabel.text = item.name distanceLabel.text = "\(item.distanceText)km" addressLabel.text = item.address if let url = URL(string: item.coverImg), !item.coverImg.isEmpty { coverImageView.kf.setImage(with: url, placeholder: UIImage(systemName: "photo")) } else { coverImageView.image = UIImage(systemName: "photo") coverImageView.tintColor = AppColor.textTertiary } if let label = item.statusLabel { statusTag.isHidden = false statusTag.text = " \(label) " switch item.openStatus { case .nearest, .closed: statusTag.backgroundColor = selectedBorderColor case .none: statusTag.backgroundColor = AppColor.textTertiary } } else { statusTag.isHidden = true } cardView.layer.borderColor = (item.isSelected ? selectedBorderColor : UIColor(hex: 0xF4F4F4)).cgColor } @objc private func cardTapped() { onTap?() } }