No Description

AlertView.swift 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. //
  2. // AlertView.swift
  3. // PaiaiUIKit
  4. //
  5. // Created by FFIB on 2017/11/14.
  6. // Copyright © 2017年 FFIB. All rights reserved.
  7. //
  8. import UIKit
  9. public final class AlertView: UIView {
  10. public static var `default`: AlertView {
  11. return AlertView()
  12. }
  13. private typealias ItemAction = ((AlertItem) -> Void)
  14. private var confirmAction: AlertAction?
  15. private var cancelAction: AlertAction?
  16. public var cancelItem: AlertItem = {
  17. let item = AlertItem(type: .custom)
  18. item.backgroundColor = UIColor(r: 214, g: 214, b: 214)
  19. item.setTitleColor(UIColor(r: 51, g: 51, b: 51), for: .normal)
  20. return item
  21. }()
  22. public var confirmItem: AlertItem = {
  23. let item = AlertItem(type: .custom)
  24. item.backgroundColor = UIColor(r: 129, g: 209, b: 53)
  25. item.setTitleColor(UIColor.white, for: .normal)
  26. return item
  27. }()
  28. public var titleLabel: UILabel = {
  29. var label = UILabel()
  30. label.numberOfLines = 0
  31. label.textAlignment = .left
  32. label.backgroundColor = UIColor.white
  33. label.font = UIFont.systemFont(ofSize: 17)
  34. label.textColor = UIColor(r: 53, g: 53, b: 53)
  35. return label
  36. }()
  37. public var messageLabel: UILabel = {
  38. var label = UILabel()
  39. label.numberOfLines = 0
  40. label.textAlignment = .left
  41. label.backgroundColor = UIColor.white
  42. label.font = UIFont.systemFont(ofSize: 15)
  43. label.textColor = UIColor(r: 153, g: 153, b: 153)
  44. return label
  45. }()
  46. public var contentView: UIView?
  47. private var viewNotReady = true
  48. private var bottomView: UIView?
  49. private var topView: UIView?
  50. var title: String = "" {
  51. didSet {
  52. titleLabel.text = title
  53. }
  54. }
  55. var message: String = "" {
  56. didSet {
  57. messageLabel.text = message
  58. }
  59. }
  60. override public func didMoveToWindow() {
  61. super.didMoveToWindow()
  62. guard viewNotReady else { return }
  63. constructViewHierarchy()
  64. activateConstraints()
  65. installTarget()
  66. backgroundColor = UIColor.white
  67. viewNotReady = false
  68. }
  69. private func constructViewHierarchy() {
  70. if !title.isEmpty { addSubview(titleLabel) }
  71. if !message.isEmpty { addSubview(messageLabel) }
  72. if cancelAction != nil { addSubview(cancelItem) }
  73. if confirmAction != nil { addSubview(confirmItem) }
  74. if let contentView = contentView { addSubview(contentView) }
  75. }
  76. private func activateConstraints() {
  77. activateConstraintsItems()
  78. activateConstraintsLabels()
  79. activateConstraintsContentView()
  80. activateConstraintsRootView()
  81. }
  82. func addAlertAction(_ action: AlertAction) {
  83. switch action.style {
  84. case .default:
  85. confirmAction = action
  86. case .cancel:
  87. cancelAction = action
  88. case let .custom(item):
  89. confirmAction = action
  90. confirmItem = item
  91. }
  92. }
  93. private func installTarget() {
  94. if cancelAction != nil {
  95. cancelItem.addTarget(self, action: #selector(cancelAction(btn:)), for: .touchDown)
  96. }
  97. if confirmAction != nil {
  98. confirmItem.addTarget(self, action: #selector(confirmAction(btn:)), for: .touchDown)
  99. }
  100. }
  101. @objc private func confirmAction(btn: UIButton) {
  102. confirmAction?.handler?(confirmItem)
  103. dismissSuperViewController()
  104. }
  105. @objc private func cancelAction(btn: UIButton) {
  106. cancelAction?.handler?(cancelItem)
  107. dismissSuperViewController()
  108. }
  109. private func dismissSuperViewController() {
  110. guard let vc = getSuperViewController() else { return }
  111. vc.dismissController()
  112. }
  113. }
  114. /// MARK: layout
  115. fileprivate extension AlertView {
  116. func activateConstraintsRootView() {
  117. guard let v = superview else { return }
  118. translatesAutoresizingMaskIntoConstraints = false
  119. NSLayoutConstraint.activate([
  120. centerYAnchor.constraint(equalTo: v.centerYAnchor),
  121. centerXAnchor.constraint(equalTo: v.centerXAnchor),
  122. leadingAnchor.constraint(equalTo: v.leadingAnchor, constant: 40),
  123. trailingAnchor.constraint(equalTo: v.trailingAnchor, constant: -40),
  124. ])
  125. if #available(iOS 11, *) {
  126. directionalLayoutMargins = NSDirectionalEdgeInsets(top: 10, leading: 16, bottom: 0, trailing: 16)
  127. } else {
  128. layoutMargins = UIEdgeInsets(top: 10, left: 16, bottom: 0, right: 16)
  129. }
  130. guard let topView = topView else { return }
  131. NSLayoutConstraint.activate([
  132. topView.bottomAnchor.constraint(equalTo: bottomView?.topAnchor ?? bottomAnchor, constant: -12)
  133. ])
  134. }
  135. func activateConstraintsContentView() {
  136. guard let contentView = contentView else { return }
  137. contentView.translatesAutoresizingMaskIntoConstraints = false
  138. NSLayoutConstraint.activate([
  139. contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
  140. contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
  141. contentView.topAnchor.constraint(equalTo: topView?.bottomAnchor ?? topAnchor),
  142. contentView.bottomAnchor.constraint(equalTo: bottomView?.bottomAnchor ?? bottomAnchor)
  143. ])
  144. bottomView = contentView
  145. }
  146. func activateConstraintsItems() {
  147. let (alertActions, items) = getActionsAndItems()
  148. guard !alertActions.isEmpty, let width = superview?.bounds.width else { return }
  149. /// center view spacing is 80
  150. /// margins 32
  151. let spacing: CGFloat = 80 + 32
  152. let itemSpacing: CGFloat = CGFloat((items.count - 1) * 6)
  153. let w = (width - spacing - itemSpacing) / CGFloat(items.count)
  154. var last: AlertItem? = nil
  155. var leading: CGFloat = 0
  156. for (action, item) in zip(alertActions, items) {
  157. item.translatesAutoresizingMaskIntoConstraints = false
  158. item.setTitle(action.title, for: .normal)
  159. NSLayoutConstraint.activate([
  160. item.widthAnchor.constraint(equalToConstant: w),
  161. item.heightAnchor.constraint(equalToConstant: 36),
  162. item.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -14),
  163. item.leadingAnchor.constraint(equalTo: last?.trailingAnchor ?? layoutMarginsGuide.leadingAnchor, constant: leading)
  164. ])
  165. leading = 6
  166. last = item
  167. }
  168. bottomView = last
  169. }
  170. func activateConstraintsLabels() {
  171. var labels: [UILabel] = []
  172. if !title.isEmpty { labels.append(titleLabel) }
  173. if !message.isEmpty { labels.append(messageLabel) }
  174. if labels.isEmpty { return }
  175. var last: UILabel? = nil
  176. for label in labels {
  177. label.translatesAutoresizingMaskIntoConstraints = false
  178. NSLayoutConstraint.activate([
  179. label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
  180. label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
  181. label.topAnchor.constraint(equalTo: last?.bottomAnchor ?? layoutMarginsGuide.topAnchor, constant: 6),
  182. ])
  183. last = label
  184. }
  185. topView = last
  186. }
  187. }
  188. fileprivate extension AlertView {
  189. func getActionsAndItems() -> ([AlertAction], [AlertItem]) {
  190. let alertActions = [cancelAction, confirmAction].compactMap { $0 }
  191. let items = alertActions.map { (action) -> AlertItem in
  192. switch action.style {
  193. case .cancel:
  194. return cancelItem
  195. case .default, .custom:
  196. return confirmItem
  197. }
  198. }
  199. return (alertActions, items)
  200. }
  201. }