LNPostSkillSelectPanel.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. //
  2. // LNPostSkillSelectPanel.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/16.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNPostSkillSelectPanel: LNPopupView {
  11. private let stackView = UIStackView()
  12. private let confirmButton = UIButton()
  13. private let maxSelection = 3
  14. private var curSelecteds: [LNGameMateSkillVO] = []
  15. var handler: (([LNGameMateSkillVO]) -> Void)?
  16. override init(frame: CGRect) {
  17. super.init(frame: frame)
  18. setupViews()
  19. }
  20. func setSkills(skills: [LNGameMateSkillVO], selecteds: [LNGameMateSkillVO]) {
  21. stackView.arrangedSubviews.forEach {
  22. stackView.removeArrangedSubview($0)
  23. $0.removeFromSuperview()
  24. }
  25. var itemViews: [UIView] = []
  26. skills.forEach { skill in
  27. let isSelected = selecteds.contains { $0.id == skill.id }
  28. let itemView = buildSkillItem(skill, selected: isSelected)
  29. itemViews.append(itemView)
  30. }
  31. for index in (1..<itemViews.count).reversed() {
  32. itemViews.insert(buildLine(), at: index)
  33. }
  34. itemViews.forEach {
  35. stackView.addArrangedSubview($0)
  36. }
  37. curSelecteds = selecteds
  38. updateConfirmButton()
  39. }
  40. required init?(coder: NSCoder) {
  41. fatalError("init(coder:) has not been implemented")
  42. }
  43. }
  44. extension LNPostSkillSelectPanel {
  45. private func updateConfirmButton() {
  46. if curSelecteds.isEmpty {
  47. confirmButton.isEnabled = false
  48. confirmButton.setBackgroundImage(nil, for: .normal)
  49. } else {
  50. confirmButton.isEnabled = true
  51. confirmButton.setBackgroundImage(.primary_8, for: .normal)
  52. }
  53. }
  54. private func setupViews() {
  55. stackView.axis = .vertical
  56. stackView.spacing = 0
  57. container.addSubview(stackView)
  58. stackView.snp.makeConstraints { make in
  59. make.leading.trailing.equalToSuperview()
  60. make.top.equalToSuperview().offset(6)
  61. }
  62. confirmButton.setTitle(.init(key: "A00223"), for: .normal)
  63. confirmButton.setTitleColor(.text_1, for: .normal)
  64. confirmButton.titleLabel?.font = .heading_h3
  65. confirmButton.setBackgroundImage(.primary_8, for: .normal)
  66. confirmButton.backgroundColor = .text_2
  67. confirmButton.layer.cornerRadius = 23.5
  68. confirmButton.clipsToBounds = true
  69. confirmButton.addAction(UIAction(handler: { [weak self] _ in
  70. guard let self else { return }
  71. dismiss()
  72. handler?(curSelecteds)
  73. }), for: .touchUpInside)
  74. container.addSubview(confirmButton)
  75. confirmButton.snp.makeConstraints { make in
  76. make.horizontalEdges.equalToSuperview().inset(16)
  77. make.top.equalTo(stackView.snp.bottom).offset(10)
  78. make.bottom.equalToSuperview().offset(commonBottomInset)
  79. make.height.equalTo(47)
  80. }
  81. }
  82. private func buildSkillItem(_ skill: LNGameMateSkillVO, selected: Bool) -> UIView {
  83. let container = UIView()
  84. container.snp.makeConstraints { make in
  85. make.height.equalTo(48)
  86. }
  87. let checkIc = UIImageView()
  88. checkIc.image = selected ? .icCheck : .icUncheck
  89. container.addSubview(checkIc)
  90. checkIc.snp.makeConstraints { make in
  91. make.centerY.equalToSuperview()
  92. make.trailing.equalToSuperview().offset(-16)
  93. }
  94. let skillIc = UIImageView()
  95. skillIc.layer.cornerRadius = 12
  96. skillIc.clipsToBounds = true
  97. skillIc.sd_setImage(with: URL(string: skill.icon))
  98. container.addSubview(skillIc)
  99. skillIc.snp.makeConstraints { make in
  100. make.leading.equalToSuperview().offset(16)
  101. make.centerY.equalToSuperview()
  102. make.width.height.equalTo(24)
  103. }
  104. let titleLabel = UILabel()
  105. titleLabel.text = skill.name
  106. titleLabel.font = .heading_h4
  107. titleLabel.textColor = .text_5
  108. container.addSubview(titleLabel)
  109. titleLabel.snp.makeConstraints { make in
  110. make.leading.equalTo(skillIc.snp.trailing).offset(8)
  111. make.centerY.equalToSuperview()
  112. make.trailing.lessThanOrEqualTo(checkIc.snp.leading).offset(-5)
  113. }
  114. container.onTap { [weak self, weak checkIc] in
  115. guard let self, let checkIc else { return }
  116. if curSelecteds.contains(where: { $0.id == skill.id }) {
  117. curSelecteds.removeAll { $0.id == skill.id }
  118. checkIc.image = .icUncheck
  119. } else {
  120. if maxSelection == curSelecteds.count {
  121. showToast(.init(key: "A00224", maxSelection))
  122. } else {
  123. curSelecteds.append(skill)
  124. checkIc.image = .icCheck
  125. }
  126. }
  127. updateConfirmButton()
  128. }
  129. return container
  130. }
  131. private func buildLine() -> UIView {
  132. let container = UIView()
  133. let line = UIView()
  134. line.backgroundColor = .fill_2
  135. container.addSubview(line)
  136. line.snp.makeConstraints { make in
  137. make.verticalEdges.equalToSuperview()
  138. make.height.equalTo(1)
  139. make.horizontalEdges.equalToSuperview().inset(16)
  140. }
  141. return container
  142. }
  143. }
  144. #if DEBUG
  145. import SwiftUI
  146. struct LNPostSkillSelectPanelPreview: UIViewRepresentable {
  147. func makeUIView(context: Context) -> some UIView {
  148. let container = UIView()
  149. container.backgroundColor = .lightGray
  150. let view = LNPostSkillSelectPanel()
  151. view.popup(container)
  152. return container
  153. }
  154. func updateUIView(_ uiView: UIViewType, context: Context) { }
  155. }
  156. #Preview(body: {
  157. LNPostSkillSelectPanelPreview()
  158. })
  159. #endif // DEBUG