LNHourRangePickerPanel.swift 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //
  2. // LNHourRangePickerPanel.swift
  3. // Gami
  4. //
  5. // Created by OneeChan on 2026/1/23.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNHourRangePickerPanel: LNPopupView {
  11. private let titleLabel = UILabel()
  12. private let descLabel = UILabel()
  13. private let fromPicker = UIPickerView()
  14. private let toPicker = UIPickerView()
  15. var isRelative: Bool = true
  16. var handler: ((Int, Int) -> Void)?
  17. override init(frame: CGRect) {
  18. super.init(frame: frame)
  19. setupViews()
  20. }
  21. func setTitles(_ title: String, desc: String? = nil) {
  22. titleLabel.text = title
  23. descLabel.text = desc
  24. descLabel.isHidden = desc?.isEmpty != false
  25. }
  26. func setDefault(from: Int, to: Int) {
  27. fromPicker.selectRow(from, inComponent: 0, animated: false)
  28. let toIndex = if isRelative {
  29. to - from - 1
  30. } else {
  31. to
  32. }
  33. toPicker.selectRow(toIndex, inComponent: 0, animated: false)
  34. }
  35. required init?(coder: NSCoder) {
  36. fatalError("init(coder:) has not been implemented")
  37. }
  38. }
  39. extension LNHourRangePickerPanel: UIPickerViewDataSource, UIPickerViewDelegate {
  40. func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
  41. if pickerView == toPicker, isRelative {
  42. 24
  43. } else {
  44. 24
  45. }
  46. }
  47. func numberOfComponents(in pickerView: UIPickerView) -> Int {
  48. 1
  49. }
  50. func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
  51. let itemView = view as? LNHourRangeItemView ?? LNHourRangeItemView()
  52. if pickerView == fromPicker {
  53. itemView.titleLabel.text = String(format: "%02d:00", row)
  54. } else if pickerView == toPicker {
  55. if isRelative {
  56. let from = fromPicker.selectedRow(inComponent: 0)
  57. itemView.titleLabel.text = String(format: "%02d:00", (row + from + 1) % 24)
  58. } else {
  59. itemView.titleLabel.text = String(format: "%02d:00", row)
  60. }
  61. }
  62. return itemView
  63. }
  64. func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
  65. if pickerView == fromPicker, isRelative {
  66. toPicker.reloadComponent(0)
  67. }
  68. }
  69. func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
  70. 52
  71. }
  72. }
  73. extension LNHourRangePickerPanel {
  74. private func setupViews() {
  75. let header = UIView()
  76. container.addSubview(header)
  77. header.snp.makeConstraints { make in
  78. make.horizontalEdges.equalToSuperview()
  79. make.top.equalToSuperview()
  80. }
  81. let stackView = UIStackView()
  82. stackView.axis = .vertical
  83. stackView.spacing = 4
  84. header.addSubview(stackView)
  85. stackView.snp.makeConstraints { make in
  86. make.horizontalEdges.equalToSuperview().inset(22)
  87. make.verticalEdges.equalToSuperview().inset(9)
  88. }
  89. titleLabel.font = .heading_h3
  90. titleLabel.textColor = .text_5
  91. titleLabel.textAlignment = .center
  92. stackView.addArrangedSubview(titleLabel)
  93. descLabel.font = .body_s
  94. descLabel.textColor = .text_4
  95. descLabel.textAlignment = .center
  96. descLabel.numberOfLines = 0
  97. stackView.addArrangedSubview(descLabel)
  98. let pickerView = UIView()
  99. container.addSubview(pickerView)
  100. pickerView.snp.makeConstraints { make in
  101. make.horizontalEdges.equalToSuperview().inset(20)
  102. make.top.equalTo(header.snp.bottom)
  103. make.height.equalTo(150)
  104. }
  105. let textView = UIView()
  106. pickerView.addSubview(textView)
  107. textView.snp.makeConstraints { make in
  108. make.center.equalToSuperview()
  109. }
  110. let toLabel = UILabel()
  111. toLabel.font = .body_l
  112. toLabel.textColor = .text_5
  113. toLabel.text = .init(key: "B00081")
  114. toLabel.textAlignment = .center
  115. toLabel.setContentHuggingPriority(.required, for: .horizontal)
  116. toLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
  117. textView.addSubview(toLabel)
  118. toLabel.snp.makeConstraints { make in
  119. make.horizontalEdges.equalToSuperview().inset(12)
  120. make.verticalEdges.equalToSuperview()
  121. }
  122. fromPicker.delegate = self
  123. fromPicker.dataSource = self
  124. pickerView.addSubview(fromPicker)
  125. fromPicker.snp.makeConstraints { make in
  126. make.verticalEdges.equalToSuperview()
  127. make.leading.equalToSuperview()
  128. make.trailing.equalTo(textView.snp.leading)
  129. }
  130. toPicker.delegate = self
  131. toPicker.dataSource = self
  132. pickerView.addSubview(toPicker)
  133. toPicker.snp.makeConstraints { make in
  134. make.verticalEdges.equalToSuperview()
  135. make.trailing.equalToSuperview()
  136. make.leading.equalTo(textView.snp.trailing)
  137. make.width.equalTo(fromPicker)
  138. }
  139. let confirm = UIButton()
  140. confirm.setTitle(.init(key: "A00002"), for: .normal)
  141. confirm.setTitleColor(.text_1, for: .normal)
  142. confirm.setBackgroundImage(.primary_8, for: .normal)
  143. confirm.layer.cornerRadius = 23.5
  144. confirm.titleLabel?.font = .heading_h3
  145. confirm.clipsToBounds = true
  146. confirm.addAction(UIAction(handler: { [weak self] _ in
  147. guard let self else { return }
  148. dismiss()
  149. let from = fromPicker.selectedRow(inComponent: 0)
  150. var to = toPicker.selectedRow(inComponent: 0)
  151. if isRelative {
  152. to += from + 1
  153. }
  154. handler?(from, to)
  155. }), for: .touchUpInside)
  156. container.addSubview(confirm)
  157. confirm.snp.makeConstraints { make in
  158. make.horizontalEdges.equalToSuperview().inset(22)
  159. make.bottom.equalToSuperview().offset(commonBottomInset)
  160. make.height.equalTo(47)
  161. make.top.equalTo(pickerView.snp.bottom).offset(4)
  162. }
  163. DispatchQueue.main.async { [weak self] in
  164. guard let self else { return }
  165. fromPicker.subviews.forEach {
  166. if $0.subviews.isEmpty {
  167. $0.backgroundColor = .clear
  168. }
  169. }
  170. toPicker.subviews.forEach {
  171. if $0.subviews.isEmpty {
  172. $0.backgroundColor = .clear
  173. }
  174. }
  175. }
  176. }
  177. }
  178. private class LNHourRangeItemView: UIView {
  179. let titleLabel = UILabel()
  180. override init(frame: CGRect) {
  181. super.init(frame: frame)
  182. titleLabel.font = .body_xl
  183. titleLabel.textColor = .text_5
  184. titleLabel.textAlignment = .center
  185. addSubview(titleLabel)
  186. titleLabel.snp.makeConstraints { make in
  187. make.center.equalToSuperview()
  188. }
  189. }
  190. required init?(coder: NSCoder) {
  191. fatalError("init(coder:) has not been implemented")
  192. }
  193. }