LNMultiSelectionPanel.swift 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. //
  2. // LNMultiSelectionPanel.swift
  3. // Gami
  4. //
  5. // Created by OneeChan on 2026/1/20.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNMultiSelectionPanel: LNPopupView {
  11. private var minCount: Int = 0
  12. private var maxCount: Int = 0
  13. private let titleLabel = UILabel()
  14. private let descLabel = UILabel()
  15. private let stackView = LNMultiLineStackView()
  16. private let confirmButton = UIButton()
  17. private var itemViews: [LNMultiSelectPanelItemView] = []
  18. private var curSelection: [Int] {
  19. var indexs: [Int] = []
  20. for (index, itemView) in itemViews.enumerated() {
  21. if itemView.isSelected {
  22. indexs.append(index)
  23. }
  24. }
  25. return indexs
  26. }
  27. var handler: (([Int]) -> Void)?
  28. override init(frame: CGRect) {
  29. super.init(frame: frame)
  30. setupViews()
  31. }
  32. func setTitles(_ title: String, desc: String? = nil) {
  33. titleLabel.text = title
  34. descLabel.text = desc
  35. descLabel.isHidden = desc?.isEmpty != false
  36. }
  37. func update(_ items: [String], curSelected: [String]) {
  38. var views: [LNMultiSelectPanelItemView] = []
  39. for item in items {
  40. let itemView = LNMultiSelectPanelItemView()
  41. itemView.titleLabel.text = item
  42. itemView.isSelected = curSelected.contains(item)
  43. itemView.onTap { [weak self, weak itemView] in
  44. guard let self, let itemView else { return }
  45. itemView.isSelected.toggle()
  46. if maxCount > 0, curSelection.count >= maxCount {
  47. itemViews.forEach {
  48. $0.isEnable = $0.isSelected
  49. }
  50. } else {
  51. itemViews.forEach {
  52. $0.isEnable = true
  53. }
  54. }
  55. updateConfirm()
  56. }
  57. views.append(itemView)
  58. }
  59. itemViews = views
  60. stackView.update(views)
  61. updateConfirm()
  62. }
  63. func setLimie(min: Int, max: Int) {
  64. minCount = min
  65. maxCount = max
  66. }
  67. required init?(coder: NSCoder) {
  68. fatalError("init(coder:) has not been implemented")
  69. }
  70. }
  71. extension LNMultiSelectionPanel {
  72. private func updateConfirm() {
  73. let isEmpty = if minCount > 0 {
  74. curSelection.count < minCount
  75. } else {
  76. curSelection.isEmpty
  77. }
  78. if isEmpty == confirmButton.isEnabled {
  79. confirmButton.isEnabled = !isEmpty
  80. confirmButton.setBackgroundImage(isEmpty ? nil : .primary_8, for: .normal)
  81. }
  82. }
  83. private func setupViews() {
  84. let headerView = buildHeader()
  85. container.addSubview(headerView)
  86. headerView.snp.makeConstraints { make in
  87. make.horizontalEdges.equalToSuperview()
  88. make.top.equalToSuperview()
  89. make.height.equalTo(56)
  90. }
  91. let selection = buildSelection()
  92. container.addSubview(selection)
  93. selection.snp.makeConstraints { make in
  94. make.horizontalEdges.equalToSuperview().inset(21)
  95. make.top.equalTo(headerView.snp.bottom).offset(16)
  96. }
  97. confirmButton.setTitle(.init(key: "A00223"), for: .normal)
  98. confirmButton.setTitleColor(.text_1, for: .normal)
  99. confirmButton.titleLabel?.font = .heading_h3
  100. confirmButton.layer.cornerRadius = 23.5
  101. confirmButton.clipsToBounds = true
  102. confirmButton.isEnabled = false
  103. confirmButton.backgroundColor = .fill_4
  104. confirmButton.addAction(UIAction(handler: { [weak self] _ in
  105. guard let self else { return }
  106. dismiss()
  107. handler?(curSelection)
  108. }), for: .touchUpInside)
  109. container.addSubview(confirmButton)
  110. confirmButton.snp.makeConstraints { make in
  111. make.horizontalEdges.equalToSuperview().inset(16)
  112. make.top.equalTo(selection.snp.bottom).offset(24)
  113. make.bottom.equalToSuperview().offset(commonBottomInset)
  114. make.height.equalTo(47)
  115. }
  116. }
  117. private func buildHeader() -> UIView {
  118. let container = UIView()
  119. let stackView = UIStackView()
  120. stackView.axis = .vertical
  121. stackView.spacing = 3
  122. container.addSubview(stackView)
  123. stackView.snp.makeConstraints { make in
  124. make.center.equalToSuperview()
  125. }
  126. titleLabel.font = .heading_h3
  127. titleLabel.textColor = .text_5
  128. titleLabel.textAlignment = .center
  129. stackView.addArrangedSubview(titleLabel)
  130. descLabel.font = .body_s
  131. descLabel.textColor = .text_3
  132. descLabel.textAlignment = .center
  133. stackView.addArrangedSubview(descLabel)
  134. return container
  135. }
  136. private func buildSelection() -> UIView {
  137. stackView.columns = 3
  138. stackView.itemSpacing = 16
  139. stackView.spacing = 20
  140. return stackView
  141. }
  142. }
  143. private class LNMultiSelectPanelItemView: UIView {
  144. let titleLabel = UILabel()
  145. var isSelected: Bool = false {
  146. didSet {
  147. backgroundColor = isSelected ? .fill_5 : .fill_1
  148. titleLabel.font = isSelected ? .heading_h5 : .body_s
  149. titleLabel.textColor = isSelected ? .text_6 : .text_4
  150. }
  151. }
  152. var isEnable: Bool = true {
  153. didSet {
  154. isUserInteractionEnabled = isEnable
  155. alpha = isEnable ? 1.0 : 0.5
  156. }
  157. }
  158. override init(frame: CGRect) {
  159. super.init(frame: frame)
  160. layer.cornerRadius = 13
  161. snp.makeConstraints { make in
  162. make.height.equalTo(26)
  163. }
  164. titleLabel.textAlignment = .center
  165. addSubview(titleLabel)
  166. titleLabel.snp.makeConstraints { make in
  167. make.centerY.equalToSuperview()
  168. make.horizontalEdges.equalToSuperview().inset(7)
  169. }
  170. isSelected = false
  171. }
  172. required init?(coder: NSCoder) {
  173. fatalError("init(coder:) has not been implemented")
  174. }
  175. }