TagPickerView.swift 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. //
  2. // TagPickerView.swift
  3. // VenusKitto
  4. //
  5. // Created by Neoa on 2025/8/25.
  6. //
  7. import Foundation
  8. import UIKit
  9. final class TagPickerView: UIView {
  10. private let overlay = UIControl()
  11. private let panel = UIView()
  12. private var tagStackView: UIStackView!
  13. private var selectedTags: Set<String> = []
  14. private let titleLabel = UILabel()
  15. private let closeButton = UIButton(type: .system)
  16. private let backButton = UIButton(type: .system)
  17. var onTagSelected: ((String) -> Void)?
  18. override init(frame: CGRect) {
  19. super.init(frame: frame)
  20. setupUI()
  21. }
  22. required init?(coder: NSCoder) {
  23. fatalError("init(coder:) has not been implemented")
  24. }
  25. private func setupUI() {
  26. backgroundColor = UIColor.black.withAlphaComponent(0.5)
  27. // overlay to close picker
  28. overlay.addTarget(self, action: #selector(closeTagPicker), for: .touchUpInside)
  29. overlay.isHidden = true
  30. overlay.alpha = 0
  31. addSubview(overlay)
  32. // panel (the actual tag picker view)
  33. panel.backgroundColor = .white
  34. panel.layer.cornerRadius = 16
  35. panel.isHidden = true
  36. panel.alpha = 0
  37. addSubview(panel)
  38. // titleLabel
  39. titleLabel.text = "选择标签"
  40. titleLabel.font = .systemFont(ofSize: 18, weight: .semibold)
  41. titleLabel.textColor = UIColor(hex: "#2B2B2B")
  42. titleLabel.textAlignment = .center
  43. // close button
  44. closeButton.setImage(UIImage(named: "AddPet384")?.withRenderingMode(.alwaysOriginal), for: .normal)
  45. closeButton.tintColor = UIColor(hex: "#2B2B2B")
  46. closeButton.addTarget(self, action: #selector(closeTagPicker), for: .touchUpInside)
  47. // back button
  48. backButton.setImage(UIImage(named: "backIcon")?.withRenderingMode(.alwaysOriginal), for: .normal)
  49. backButton.tintColor = UIColor(hex: "#2B2B2B")
  50. backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
  51. // Add title, back and close buttons to panel
  52. [backButton, titleLabel, closeButton].forEach { panel.addSubview($0) }
  53. // Add tagStackView to the panel
  54. tagStackView = UIStackView()
  55. tagStackView.axis = .vertical
  56. tagStackView.spacing = 16
  57. panel.addSubview(tagStackView)
  58. // Layout constraints
  59. overlay.translatesAutoresizingMaskIntoConstraints = false
  60. panel.translatesAutoresizingMaskIntoConstraints = false
  61. titleLabel.translatesAutoresizingMaskIntoConstraints = false
  62. closeButton.translatesAutoresizingMaskIntoConstraints = false
  63. backButton.translatesAutoresizingMaskIntoConstraints = false
  64. tagStackView.translatesAutoresizingMaskIntoConstraints = false
  65. NSLayoutConstraint.activate([
  66. overlay.topAnchor.constraint(equalTo: topAnchor),
  67. overlay.leadingAnchor.constraint(equalTo: leadingAnchor),
  68. overlay.trailingAnchor.constraint(equalTo: trailingAnchor),
  69. overlay.bottomAnchor.constraint(equalTo: bottomAnchor),
  70. panel.centerXAnchor.constraint(equalTo: centerXAnchor),
  71. panel.centerYAnchor.constraint(equalTo: centerYAnchor),
  72. panel.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1),
  73. backButton.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 12),
  74. backButton.topAnchor.constraint(equalTo: panel.topAnchor, constant: 12),
  75. backButton.widthAnchor.constraint(equalToConstant: 24),
  76. backButton.heightAnchor.constraint(equalToConstant: 24),
  77. titleLabel.topAnchor.constraint(equalTo: panel.topAnchor, constant: 12),
  78. titleLabel.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
  79. closeButton.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -12),
  80. closeButton.topAnchor.constraint(equalTo: panel.topAnchor, constant: 12),
  81. closeButton.widthAnchor.constraint(equalToConstant: 24),
  82. closeButton.heightAnchor.constraint(equalToConstant: 24),
  83. tagStackView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 16),
  84. tagStackView.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 16),
  85. tagStackView.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -16),
  86. tagStackView.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -16)
  87. ])
  88. buildTagSection()
  89. }
  90. private func buildTagSection() {
  91. let categories: [(String, [String])] = [
  92. ("日常事项", ["吃饭", "喝水", "喂奶", "尿便", "铲屎"]),
  93. ("健康事项", ["驱虫", "喂药", "疫苗", "看病", "体检"]),
  94. ("清洁事项", ["洗澡", "梳毛", "剪指甲", "洗耳朵", "挤肛门腺"]),
  95. ("打扫事项", ["洗笼子","洗食盆","洗水盆","洗玩具","换耗材"])
  96. ]
  97. for (category, items) in categories {
  98. let sectionView = createTagSection(title: category, items: items)
  99. tagStackView.addArrangedSubview(sectionView)
  100. }
  101. }
  102. private func createTagSection(title: String, items: [String]) -> UIView {
  103. let sectionView = UIView()
  104. let sectionTitle = UILabel()
  105. sectionTitle.text = title
  106. sectionTitle.font = .systemFont(ofSize: 13, weight: .semibold)
  107. sectionTitle.textColor = UIColor(hex: "#000000")
  108. sectionView.addSubview(sectionTitle)
  109. let tagsStackView = UIStackView()
  110. tagsStackView.axis = .horizontal
  111. tagsStackView.spacing = 12
  112. tagsStackView.distribution = .fillEqually
  113. sectionView.addSubview(tagsStackView)
  114. sectionTitle.translatesAutoresizingMaskIntoConstraints = false
  115. tagsStackView.translatesAutoresizingMaskIntoConstraints = false
  116. NSLayoutConstraint.activate([
  117. sectionTitle.topAnchor.constraint(equalTo: sectionView.topAnchor),
  118. sectionTitle.leadingAnchor.constraint(equalTo: sectionView.leadingAnchor),
  119. sectionTitle.trailingAnchor.constraint(equalTo: sectionView.trailingAnchor),
  120. sectionTitle.heightAnchor.constraint(equalToConstant: 20),
  121. tagsStackView.topAnchor.constraint(equalTo: sectionTitle.bottomAnchor, constant: 8),
  122. tagsStackView.leadingAnchor.constraint(equalTo: sectionView.leadingAnchor),
  123. tagsStackView.trailingAnchor.constraint(equalTo: sectionView.trailingAnchor),
  124. tagsStackView.bottomAnchor.constraint(equalTo: sectionView.bottomAnchor)
  125. ])
  126. for tag in items {
  127. let tagButton = createTagButton(title: tag)
  128. tagsStackView.addArrangedSubview(tagButton)
  129. }
  130. return sectionView
  131. }
  132. private func createTagButton(title: String) -> UIView {
  133. let holder = UIView()
  134. // Create the image view for the tag icon (peihead)
  135. let imageView = UIImageView(image: UIImage(named: "peihead"))
  136. imageView.contentMode = .scaleAspectFit
  137. imageView.translatesAutoresizingMaskIntoConstraints = false
  138. imageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
  139. imageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
  140. // Create the label for the tag title
  141. let label = UILabel()
  142. label.text = title
  143. label.font = .systemFont(ofSize: 12)
  144. label.textColor = UIColor(hex: "#6B6B6B")
  145. label.textAlignment = .center
  146. label.translatesAutoresizingMaskIntoConstraints = false
  147. // Stack the image and label vertically
  148. let stackView = UIStackView(arrangedSubviews: [imageView, label])
  149. stackView.axis = .vertical
  150. stackView.alignment = .center
  151. stackView.spacing = 6
  152. stackView.translatesAutoresizingMaskIntoConstraints = false
  153. holder.addSubview(stackView)
  154. NSLayoutConstraint.activate([
  155. stackView.topAnchor.constraint(equalTo: holder.topAnchor),
  156. stackView.bottomAnchor.constraint(equalTo: holder.bottomAnchor),
  157. stackView.leadingAnchor.constraint(equalTo: holder.leadingAnchor),
  158. stackView.trailingAnchor.constraint(equalTo: holder.trailingAnchor)
  159. ])
  160. // Full-size invisible button to capture taps
  161. let tap = UIButton(type: .system)
  162. tap.backgroundColor = .clear
  163. tap.accessibilityLabel = title
  164. tap.addTarget(self, action: #selector(tagButtonTapped(_:)), for: .touchUpInside)
  165. holder.addSubview(tap)
  166. tap.translatesAutoresizingMaskIntoConstraints = false
  167. NSLayoutConstraint.activate([
  168. tap.topAnchor.constraint(equalTo: holder.topAnchor),
  169. tap.bottomAnchor.constraint(equalTo: holder.bottomAnchor),
  170. tap.leadingAnchor.constraint(equalTo: holder.leadingAnchor),
  171. tap.trailingAnchor.constraint(equalTo: holder.trailingAnchor)
  172. ])
  173. return holder
  174. }
  175. @objc private func tagButtonTapped(_ sender: UIButton) {
  176. guard let tag = sender.accessibilityLabel else { return }
  177. onTagSelected?(tag)
  178. // Handle the tag selection here
  179. print("Selected tag: \(tag)")
  180. hide()
  181. }
  182. func show() {
  183. overlay.isHidden = false
  184. panel.isHidden = false
  185. UIView.animate(withDuration: 0.2) {
  186. self.overlay.alpha = 1
  187. self.panel.alpha = 1
  188. }
  189. }
  190. func hide() {
  191. UIView.animate(withDuration: 0.2, animations: {
  192. self.overlay.alpha = 0
  193. self.panel.alpha = 0
  194. }) { _ in
  195. self.overlay.isHidden = true
  196. self.panel.isHidden = true
  197. self.isHidden = true
  198. }
  199. }
  200. @objc private func closeTagPicker() {
  201. hide()
  202. }
  203. @objc private func backButtonTapped() {
  204. // Handle the back button logic here
  205. print("Back button tapped")
  206. }
  207. }