LNSkillFieldPriceEditView.swift 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. //
  2. // LNSkillFieldPriceEditView.swift
  3. // Gami
  4. //
  5. // Created by OneeChan on 2026/1/25.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNSkillFieldPriceEditView: LNSkillFieldBaseEditView {
  11. private let curValueLabel = UILabel()
  12. override init(frame: CGRect) {
  13. super.init(frame: frame)
  14. setupViews()
  15. }
  16. override func update(_ field: LNSkillEditField) {
  17. super.update(field)
  18. if let text = field.value as? Double {
  19. curValueLabel.text = text.toDisplay
  20. } else {
  21. curValueLabel.text = "0"
  22. }
  23. }
  24. required init(coder: NSCoder) {
  25. fatalError("init(coder:) has not been implemented")
  26. }
  27. }
  28. extension LNSkillFieldPriceEditView {
  29. private func setupViews() {
  30. container.backgroundColor = .fill_2
  31. container.layer.cornerRadius = 19
  32. container.onTap { [weak self] in
  33. guard let self else { return }
  34. guard let field else { return }
  35. let panel = LNSkillPriceEditPanel()
  36. panel.titleLabel.text = field.fieldName
  37. panel.inputField.text = (field.value as? Double)?.toDisplay
  38. panel.handleTextFieldDidChanged()
  39. panel.limit = field.validate.numLimit
  40. panel.handler = { [weak self] price in
  41. guard let self else { return }
  42. curValueLabel.text = price.toDisplay
  43. field.value = price
  44. delegate?.onSkillFieldBaseEditViewInputChanged(view: self)
  45. }
  46. panel.popup()
  47. }
  48. container.snp.makeConstraints { make in
  49. make.height.equalTo(38)
  50. }
  51. let arrow = UIImageView.arrowImageView(size: 15)
  52. arrow.tintColor = .text_3
  53. container.addSubview(arrow)
  54. arrow.snp.makeConstraints { make in
  55. make.centerY.equalToSuperview()
  56. make.trailing.equalToSuperview().offset(-16)
  57. }
  58. let coin = UIImageView.coinImageView()
  59. container.addSubview(coin)
  60. coin.snp.makeConstraints { make in
  61. make.centerY.equalToSuperview()
  62. make.leading.equalToSuperview().offset(16)
  63. make.width.height.equalTo(18)
  64. }
  65. curValueLabel.font = .heading_h4
  66. curValueLabel.textColor = .text_5
  67. container.addSubview(curValueLabel)
  68. curValueLabel.snp.makeConstraints { make in
  69. make.leading.equalTo(coin.snp.trailing).offset(1)
  70. make.centerY.equalToSuperview()
  71. }
  72. }
  73. }
  74. private class LNSkillPriceEditPanel: LNPopupView, UITextFieldDelegate {
  75. let titleLabel = UILabel()
  76. let inputField = LNTextField()
  77. var limit: LNSkillFieldValidateNumLimit?
  78. private let confirmButton = UIButton()
  79. var handler: ((Double) -> Void)?
  80. override init(frame: CGRect) {
  81. super.init(frame: frame)
  82. let headerView = UIView()
  83. headerView.isUserInteractionEnabled = false
  84. container.addSubview(headerView)
  85. headerView.snp.makeConstraints { make in
  86. make.horizontalEdges.equalToSuperview()
  87. make.top.equalToSuperview()
  88. make.height.equalTo(50)
  89. }
  90. titleLabel.font = .heading_h3
  91. titleLabel.textColor = .text_5
  92. titleLabel.textAlignment = .center
  93. headerView.addSubview(titleLabel)
  94. titleLabel.snp.makeConstraints { make in
  95. make.centerY.equalToSuperview()
  96. make.horizontalEdges.equalToSuperview().inset(16)
  97. }
  98. let inputView = buildInputView()
  99. container.addSubview(inputView)
  100. inputView.snp.makeConstraints { make in
  101. make.centerX.equalToSuperview()
  102. make.top.equalTo(headerView.snp.bottom).offset(12)
  103. make.leading.greaterThanOrEqualToSuperview().offset(32)
  104. }
  105. confirmButton.setTitle(.init(key: "A00002"), for: .normal)
  106. confirmButton.setTitleColor(.text_1, for: .normal)
  107. confirmButton.titleLabel?.font = .heading_h3
  108. confirmButton.layer.cornerRadius = 23.5
  109. confirmButton.clipsToBounds = true
  110. confirmButton.isEnabled = false
  111. confirmButton.backgroundColor = .fill_4
  112. confirmButton.addAction(UIAction(handler: { [weak self] _ in
  113. guard let self else { return }
  114. let text = inputField.text ?? "0"
  115. let value = Double(text) ?? 0
  116. if let limit, value < limit.min || value > limit.max {
  117. showToast(.init(key: "B00102", limit.min.toDisplay, limit.max.toDisplay))
  118. return
  119. }
  120. dismiss()
  121. handler?(Double(inputField.text ?? "0") ?? 0)
  122. }), for: .touchUpInside)
  123. container.addSubview(confirmButton)
  124. confirmButton.snp.makeConstraints { make in
  125. make.horizontalEdges.equalToSuperview().inset(16)
  126. make.top.equalTo(inputView.snp.bottom).offset(40)
  127. make.bottom.equalToSuperview().offset(commonBottomInset)
  128. make.height.equalTo(47)
  129. }
  130. }
  131. private func buildInputView() -> UIView {
  132. let container = UIView()
  133. container.onTap { [weak self] in
  134. guard let self else { return }
  135. inputField.becomeFirstResponder()
  136. }
  137. container.snp.makeConstraints { make in
  138. make.width.greaterThanOrEqualTo(180)
  139. }
  140. let inputView = UIView()
  141. container.addSubview(inputView)
  142. inputView.snp.makeConstraints { make in
  143. make.centerX.equalToSuperview()
  144. make.verticalEdges.equalToSuperview()
  145. make.leading.greaterThanOrEqualToSuperview()
  146. }
  147. inputField.font = .heading_h1
  148. inputField.textColor = .text_5
  149. inputField.placeholder = .init(key: "B00097")
  150. inputField.keyboardType = .decimalPad
  151. inputField.delegate = self
  152. inputField.returnKeyType = .done
  153. inputField.visibleView = container
  154. inputField.cursorHeight = 18
  155. inputField.cursorWidth = 2
  156. inputField.tintColor = .primary_5
  157. inputField.addTarget(self, action: #selector(handleTextFieldDidChanged), for: .editingChanged)
  158. inputView.addSubview(inputField)
  159. inputField.snp.makeConstraints { make in
  160. make.top.equalToSuperview()
  161. make.bottom.equalToSuperview().offset(-20)
  162. make.trailing.equalToSuperview()
  163. }
  164. let coin = UIImageView.coinImageView()
  165. inputView.addSubview(coin)
  166. coin.snp.makeConstraints { make in
  167. make.leading.equalToSuperview()
  168. make.centerY.equalTo(inputField)
  169. make.width.height.equalTo(24)
  170. make.trailing.equalTo(inputField.snp.leading).offset(-2)
  171. }
  172. let line = UIView()
  173. line.backgroundColor = .fill_2
  174. container.addSubview(line)
  175. line.snp.makeConstraints { make in
  176. make.horizontalEdges.equalToSuperview()
  177. make.bottom.equalToSuperview().offset(-18)
  178. make.height.equalTo(1)
  179. }
  180. return container
  181. }
  182. required init?(coder: NSCoder) {
  183. fatalError("init(coder:) has not been implemented")
  184. }
  185. func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
  186. guard let limit else { return true }
  187. let currentText = textField.text ?? ""
  188. guard let range = Range(range, in: currentText) else { return false }
  189. let newText = currentText.replacingCharacters(in: range, with: string)
  190. if newText.isEmpty {
  191. return true
  192. }
  193. if newText.starts(with: "00") {
  194. return false
  195. }
  196. if newText.filter({ $0 == "." }).count > 2 {
  197. return false
  198. }
  199. guard let value = Double(newText) else {
  200. return false
  201. }
  202. if value > limit.max {
  203. showToast(.init(key: "B00102", limit.min.toDisplay, limit.max.toDisplay))
  204. return false
  205. }
  206. return true
  207. }
  208. func textFieldShouldReturn(_ textField: UITextField) -> Bool {
  209. textField.resignFirstResponder()
  210. return true
  211. }
  212. @objc
  213. func handleTextFieldDidChanged() {
  214. inputField.placeholder = inputField.text?.isEmpty == false ? "" : .init(key: "B00097")
  215. let inputAvailable = if let text = inputField.text, Double(text) != nil {
  216. true
  217. } else {
  218. false
  219. }
  220. if inputAvailable != confirmButton.isEnabled {
  221. confirmButton.isEnabled = inputAvailable
  222. confirmButton.setBackgroundImage(inputAvailable ? .primary_8 : nil, for: .normal)
  223. }
  224. }
  225. }