LNCommonAlertView.swift 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. //
  2. // LNCommonAlertView.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/12.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. extension LNCommonAlertView {
  11. static func showCommonDeleteAlert(handler: (() -> Void)?) {
  12. let alert = LNCommonAlertView()
  13. alert.titleLabel.text = .init(key: "A00308")
  14. alert.showConfirm {
  15. handler?()
  16. }
  17. alert.showCancel()
  18. alert.popup()
  19. }
  20. }
  21. class LNCommonAlertView: UIView {
  22. private let background = UIView()
  23. private let container = UIView()
  24. private let messageViews = UIStackView()
  25. private let buttonViews = UIStackView()
  26. private let miniScale = 0.01
  27. private let animateDuration = 0.15
  28. let titleLabel = UILabel()
  29. let messageLabel = UILabel()
  30. var touchOutsideCancel = true
  31. override init(frame: CGRect) {
  32. super.init(frame: frame)
  33. setupViews()
  34. }
  35. func showConfirm(_ title: String = .init(key: "A00002"),
  36. handler: @escaping () -> Void) {
  37. let button = buildConfirmButton()
  38. button.setTitle(title, for: .normal)
  39. button.addAction(UIAction(handler: { [weak self] _ in
  40. guard let self else { return }
  41. dismiss()
  42. handler()
  43. }), for: .touchUpInside)
  44. buttonViews.insertArrangedSubview(button, at: 0)
  45. }
  46. func showCancel(_ title: String = .init(key: "A00003"),
  47. handler: (() -> Void)? = nil) {
  48. let button = buildCancelButton()
  49. button.setTitle(title, for: .normal)
  50. button.addAction(UIAction(handler: { [weak self] _ in
  51. guard let self else { return }
  52. dismiss()
  53. handler?()
  54. }), for: .touchUpInside)
  55. buttonViews.addArrangedSubview(button)
  56. }
  57. required init?(coder: NSCoder) {
  58. fatalError("init(coder:) has not been implemented")
  59. }
  60. }
  61. extension LNCommonAlertView {
  62. func popup(_ holder: UIView? = nil) {
  63. guard let holder = holder ?? UIView.appKeyWindow else { return }
  64. holder.addSubview(self)
  65. frame = holder.bounds
  66. titleLabel.isHidden = titleLabel.text?.isEmpty != false
  67. messageLabel.isHidden = messageLabel.text?.isEmpty != false
  68. layoutIfNeeded()
  69. container.transform = .init(scaleX: miniScale, y: miniScale)
  70. UIView.animate(withDuration: animateDuration, delay: 0,
  71. options: .curveEaseInOut)
  72. { [weak self] in
  73. guard let self else { return }
  74. container.transform = .identity
  75. }
  76. }
  77. func dismiss() {
  78. UIView.animate(withDuration: animateDuration, delay: 0,
  79. options: .curveEaseInOut)
  80. { [weak self] in
  81. guard let self else { return }
  82. container.transform = .init(scaleX: miniScale, y: miniScale)
  83. } completion: { [weak self] _ in
  84. guard let self else { return }
  85. removeFromSuperview()
  86. }
  87. }
  88. }
  89. extension LNCommonAlertView {
  90. private func setupViews() {
  91. background.backgroundColor = .black.withAlphaComponent(0.4)
  92. background.onTap { [weak self] in
  93. guard let self else { return }
  94. guard touchOutsideCancel else { return }
  95. dismiss()
  96. }
  97. addSubview(background)
  98. background.snp.makeConstraints { make in
  99. make.edges.equalToSuperview()
  100. }
  101. container.backgroundColor = .fill
  102. container.layer.cornerRadius = 20
  103. addSubview(container)
  104. container.snp.makeConstraints { make in
  105. make.center.equalToSuperview()
  106. make.width.equalToSuperview().multipliedBy(0.8)
  107. }
  108. let closeButton = UIButton()
  109. closeButton.setImage(.init(systemName: "xmark"), for: .normal)
  110. closeButton.tintColor = .text_2
  111. closeButton.addAction(UIAction(handler: { [weak self] _ in
  112. guard let self else { return }
  113. dismiss()
  114. }), for: .touchUpInside)
  115. container.addSubview(closeButton)
  116. closeButton.snp.makeConstraints { make in
  117. make.top.equalToSuperview().offset(10)
  118. make.trailing.equalToSuperview().offset(-10)
  119. make.width.height.equalTo(24)
  120. }
  121. messageViews.axis = .vertical
  122. messageViews.spacing = 10
  123. container.addSubview(messageViews)
  124. messageViews.snp.makeConstraints { make in
  125. make.horizontalEdges.equalToSuperview().inset(24)
  126. make.top.equalToSuperview().offset(30)
  127. }
  128. titleLabel.font = .heading_h3
  129. titleLabel.textColor = .text_4
  130. titleLabel.textAlignment = .center
  131. titleLabel.numberOfLines = 0
  132. messageViews.addArrangedSubview(titleLabel)
  133. messageLabel.font = .body_m
  134. messageLabel.textColor = .text_4
  135. messageLabel.textAlignment = .center
  136. messageLabel.numberOfLines = 0
  137. messageViews.addArrangedSubview(messageLabel)
  138. buttonViews.axis = .vertical
  139. buttonViews.spacing = 16
  140. container.addSubview(buttonViews)
  141. buttonViews.snp.makeConstraints { make in
  142. make.horizontalEdges.equalToSuperview().inset(50)
  143. make.top.equalTo(messageViews.snp.bottom).offset(16)
  144. make.bottom.equalToSuperview().offset(-30)
  145. }
  146. }
  147. private func buildConfirmButton() -> UIButton {
  148. let button = UIButton()
  149. button.setBackgroundImage(.primary_8, for: .normal)
  150. button.setTitleColor(.text_1, for: .normal)
  151. button.titleLabel?.font = .heading_h4
  152. button.layer.cornerRadius = 18
  153. button.clipsToBounds = true
  154. button.snp.makeConstraints { make in
  155. make.height.equalTo(36)
  156. }
  157. return button
  158. }
  159. private func buildCancelButton() -> UIButton {
  160. let button = UIButton()
  161. button.setTitleColor(.text_4, for: .normal)
  162. button.titleLabel?.font = .body_m
  163. button.layer.cornerRadius = 18
  164. button.layer.borderWidth = 1
  165. button.layer.borderColor = UIColor.text_3.cgColor
  166. button.clipsToBounds = true
  167. button.snp.makeConstraints { make in
  168. make.height.equalTo(36)
  169. }
  170. return button
  171. }
  172. }
  173. #if DEBUG
  174. import SwiftUI
  175. struct LNCommonAlertViewPreview: UIViewRepresentable {
  176. func makeUIView(context: Context) -> some UIView {
  177. let container = UIView()
  178. container.backgroundColor = .lightGray
  179. let button = UIButton()
  180. button.backgroundColor = .red
  181. button.addAction(UIAction(handler: { [weak container] _ in
  182. guard let container else { return }
  183. let view = LNCommonAlertView()
  184. view.popup(container)
  185. }), for: .touchUpInside)
  186. container.addSubview(button)
  187. button.snp.makeConstraints { make in
  188. make.center.equalToSuperview()
  189. make.width.height.equalTo(50)
  190. }
  191. return container
  192. }
  193. func updateUIView(_ uiView: UIViewType, context: Context) { }
  194. }
  195. #Preview(body: {
  196. LNCommonAlertViewPreview()
  197. })
  198. #endif