UIView+NXBadgeView.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. //
  2. // UIView+NXBadgeView.swift
  3. // NXFramework-Swift
  4. //
  5. // Created by ak on 2020/11/11.
  6. //
  7. import UIKit
  8. private var kBadgeView = "kNXBadgeView"
  9. // MARK: - add Badge
  10. public extension NX where Base: UIView {
  11. var badgeView: NXBadgeControl {
  12. return base.badgeView
  13. }
  14. /// 添加带文本内容的Badge, 默认右上角, 红色, 18pts
  15. ///
  16. /// Add Badge with text content, the default upper right corner, red backgroundColor, 18pts
  17. ///
  18. /// - Parameter text: 文本字符串
  19. func addBadge(text: String?) {
  20. showBadge()
  21. base.badgeView.text = text
  22. setBadge(flexMode: base.badgeView.flexMode)
  23. if text == nil {
  24. if base.badgeView.widthConstraint()?.relation == .equal { return }
  25. base.badgeView.widthConstraint()?.isActive = false
  26. let constraint = NSLayoutConstraint(item: base.badgeView, attribute: .width, relatedBy: .equal, toItem: base.badgeView, attribute: .height, multiplier: 1.0, constant: 0)
  27. base.badgeView.addConstraint(constraint)
  28. } else {
  29. if base.badgeView.widthConstraint()?.relation == .greaterThanOrEqual { return }
  30. base.badgeView.widthConstraint()?.isActive = false
  31. let constraint = NSLayoutConstraint(item: base.badgeView, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: base.badgeView, attribute: .height, multiplier: 1.0, constant: 0)
  32. base.badgeView.addConstraint(constraint)
  33. }
  34. }
  35. /// 添加带数字的Badge, 默认右上角,红色,18pts
  36. ///
  37. /// Add the Badge with numbers, the default upper right corner, red backgroundColor, 18pts
  38. ///
  39. /// - Parameter number: 整形数字
  40. func addBadge(number: Int) {
  41. if number <= 0 {
  42. addBadge(text: "0")
  43. hiddenBadge()
  44. return
  45. }
  46. addBadge(text: "\(number)")
  47. }
  48. /// 添加带颜色的小圆点, 默认右上角, 红色, 8pts
  49. ///
  50. /// Add small dots with color, the default upper right corner, red backgroundColor, 8pts
  51. ///
  52. /// - Parameter color: 颜色
  53. func addDot(color: UIColor? = .red) {
  54. addBadge(text: nil)
  55. setBadge(height: 8.0)
  56. base.badgeView.backgroundColor = color
  57. }
  58. /// 设置Badge的偏移量, Badge中心点默认为其父视图的右上角
  59. ///
  60. /// Set Badge offset, Badge center point defaults to the top right corner of its parent view
  61. ///
  62. /// - Parameters:
  63. /// - x: X轴偏移量 (x<0: 左移, x>0: 右移) axis offset (x <0: left, x> 0: right)
  64. /// - y: Y轴偏移量 (y<0: 上移, y>0: 下移) axis offset (Y <0: up, y> 0: down)
  65. func moveBadge(x: CGFloat, y: CGFloat) {
  66. base.badgeView.offset = CGPoint(x: x, y: y)
  67. base.centerYConstraint(with: base.badgeView)?.constant = y
  68. let badgeHeight = base.badgeView.heightConstraint()?.constant ?? 0
  69. switch base.badgeView.flexMode {
  70. case .head:
  71. base.centerXConstraint(with: base.badgeView)?.isActive = false
  72. base.leadingConstraint(with: base.badgeView)?.isActive = false
  73. if let constraint = base.trailingConstraint(with: base.badgeView) {
  74. constraint.constant = badgeHeight * 0.5 + x
  75. return
  76. }
  77. let trailingConstraint = NSLayoutConstraint(item: base.badgeView, attribute: .trailing, relatedBy: .equal, toItem: base, attribute: .trailing, multiplier: 1.0, constant: badgeHeight * 0.5 + x)
  78. base.addConstraint(trailingConstraint)
  79. case .tail:
  80. base.centerXConstraint(with: base.badgeView)?.isActive = false
  81. base.trailingConstraint(with: base.badgeView)?.isActive = false
  82. if let constraint = base.leadingConstraint(with: base.badgeView) {
  83. constraint.constant = x - badgeHeight * 0.5
  84. return
  85. }
  86. let leadingConstraint = NSLayoutConstraint(item: base.badgeView, attribute: .leading, relatedBy: .equal, toItem: base, attribute: .trailing, multiplier: 1.0, constant: x - badgeHeight * 0.5)
  87. base.addConstraint(leadingConstraint)
  88. case .middle:
  89. base.leadingConstraint(with: base.badgeView)?.isActive = false
  90. base.trailingConstraint(with: base.badgeView)?.isActive = false
  91. base.centerXConstraint(with: base.badgeView)?.constant = x
  92. if let constraint = base.centerXConstraint(with: base.badgeView) {
  93. constraint.constant = x
  94. return
  95. }
  96. let centerXConstraint = NSLayoutConstraint(item: base.badgeView, attribute: .centerX, relatedBy: .equal, toItem: base, attribute: .centerX, multiplier: 1.0, constant: x)
  97. base.addConstraint(centerXConstraint)
  98. }
  99. }
  100. /// 设置Badge伸缩的方向
  101. ///
  102. /// Setting the direction of Badge expansion
  103. ///
  104. /// NXBadgeViewFlexModeHead, 左伸缩 Head Flex : <==●
  105. /// NXBadgeViewFlexModeTail, 右伸缩 Tail Flex : ●==>
  106. /// NXBadgeViewFlexModeMiddle 左右伸缩 Middle Flex : <=●=>
  107. /// - Parameter flexMode : Default is PPBadgeViewFlexModeTail
  108. func setBadge(flexMode: NXBadgeViewFlexMode = .tail) {
  109. base.badgeView.flexMode = flexMode
  110. moveBadge(x: base.badgeView.offset.x, y: base.badgeView.offset.y)
  111. }
  112. /// 设置Badge的高度,因为Badge宽度是动态可变的,通过改变Badge高度,其宽度也按比例变化,方便布局
  113. ///
  114. /// (注意: 此方法需要将Badge添加到控件上后再调用!!!)
  115. ///
  116. /// Set the height of Badge, because the Badge width is dynamically and  variable.By changing the Badge height in proportion to facilitate the layout.
  117. ///
  118. /// (Note: this method needs to add Badge to the controls and then use it !!!)
  119. ///
  120. /// - Parameter height: 高度大小
  121. func setBadge(height: CGFloat) {
  122. base.badgeView.layer.cornerRadius = height * 0.5
  123. base.badgeView.heightConstraint()?.constant = height
  124. moveBadge(x: base.badgeView.offset.x, y: base.badgeView.offset.y)
  125. }
  126. /// 显示Badge
  127. func showBadge() {
  128. base.badgeView.isHidden = false
  129. }
  130. /// 隐藏Badge
  131. func hiddenBadge() {
  132. base.badgeView.isHidden = true
  133. }
  134. // MARK: - 数字增加/减少, 注意:以下方法只适用于Badge内容为纯数字的情况
  135. // MARK: - Digital increase /decrease, note: the following method applies only to cases where the Badge content is purely numeric
  136. /// badge数字加1
  137. func increase() {
  138. increaseBy(number: 1)
  139. }
  140. /// badge数字加number
  141. func increaseBy(number: Int) {
  142. let label = base.badgeView
  143. let result = (Int(label.text ?? "0") ?? 0) + number
  144. if result > 0 {
  145. showBadge()
  146. }
  147. label.text = "\(result)"
  148. }
  149. /// badge数字加1
  150. func decrease() {
  151. decreaseBy(number: 1)
  152. }
  153. /// badge数字减number
  154. func decreaseBy(number: Int) {
  155. let label = base.badgeView
  156. let result = (Int(label.text ?? "0") ?? 0) - number
  157. if (result <= 0) {
  158. hiddenBadge()
  159. label.text = "0"
  160. return
  161. }
  162. label.text = "\(result)"
  163. }
  164. }
  165. extension UIView {
  166. public func addBadgeViewLayoutConstraint() {
  167. badgeView.translatesAutoresizingMaskIntoConstraints = false
  168. let centerXConstraint = NSLayoutConstraint(item: badgeView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 0)
  169. let centerYConstraint = NSLayoutConstraint(item: badgeView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0)
  170. let widthConstraint = NSLayoutConstraint(item: badgeView, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: badgeView, attribute: .height, multiplier: 1.0, constant: 0)
  171. let heightConstraint = NSLayoutConstraint(item: badgeView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 18)
  172. addConstraints([centerXConstraint, centerYConstraint])
  173. badgeView.addConstraints([widthConstraint, heightConstraint])
  174. }
  175. }
  176. // MARK: - getter/setter
  177. extension UIView {
  178. public var badgeView: NXBadgeControl {
  179. get {
  180. if let aValue = objc_getAssociatedObject(self, &kBadgeView) as? NXBadgeControl {
  181. return aValue
  182. }
  183. else {
  184. let badgeControl = NXBadgeControl.default()
  185. self.addSubview(badgeControl)
  186. self.bringSubviewToFront(badgeControl)
  187. self.badgeView = badgeControl
  188. self.addBadgeViewLayoutConstraint()
  189. return badgeControl
  190. }
  191. }
  192. set {
  193. objc_setAssociatedObject(self, &kBadgeView, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  194. }
  195. }
  196. public func topConstraint(with item: AnyObject?) -> NSLayoutConstraint? {
  197. return constraint(with: item, attribute: .top)
  198. }
  199. public func leadingConstraint(with item: AnyObject?) -> NSLayoutConstraint? {
  200. return constraint(with: item, attribute: .leading)
  201. }
  202. public func bottomConstraint(with item: AnyObject?) -> NSLayoutConstraint? {
  203. return constraint(with: item, attribute: .bottom)
  204. }
  205. public func trailingConstraint(with item: AnyObject?) -> NSLayoutConstraint? {
  206. return constraint(with: item, attribute: .trailing)
  207. }
  208. public func widthConstraint() -> NSLayoutConstraint? {
  209. return constraint(with: self, attribute: .width)
  210. }
  211. public func heightConstraint() -> NSLayoutConstraint? {
  212. return constraint(with: self, attribute: .height)
  213. }
  214. public func centerXConstraint(with item: AnyObject?) -> NSLayoutConstraint? {
  215. return constraint(with: item, attribute: .centerX)
  216. }
  217. public func centerYConstraint(with item: AnyObject?) -> NSLayoutConstraint? {
  218. return constraint(with: item, attribute: .centerY)
  219. }
  220. public func constraint(with item: AnyObject?, attribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? {
  221. for constraint in constraints {
  222. if let isSame = constraint.firstItem?.isEqual(item), isSame, constraint.firstAttribute == attribute {
  223. return constraint
  224. }
  225. }
  226. return nil
  227. }
  228. }