UIButton+Extension.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. //
  2. // UIButton+Extension.swift
  3. // QuickSearchLocation
  4. //
  5. // Created by mac on 2024/4/11.
  6. //
  7. import UIKit
  8. extension UIButton {
  9. enum ThemeButtonType {
  10. case mainTheme
  11. }
  12. // MARK: 1.1、创建一个带颜色的 Button, 默认为主题色,默认高度44
  13. /// 创建一个带颜色的 Button
  14. /// - Parameters:
  15. /// - type: 类型
  16. /// - height: 高度
  17. /// - Returns: 返回自身
  18. @discardableResult
  19. static func normal() -> UIButton {
  20. let normalColor: UIColor
  21. let disabledColor: UIColor
  22. let titleColorNormal: UIColor
  23. let titleColorDisable: UIColor
  24. normalColor = QSLColor.themeMainColor
  25. disabledColor = QSLColor.buttonDisabledColor
  26. titleColorNormal = .white
  27. titleColorDisable = .white
  28. let btn = UIButton(type: .custom).font(.systemFont(ofSize: 16))
  29. btn.setTitleColor(titleColorNormal, for: .normal)
  30. btn.setTitleColor(titleColorDisable, for: .disabled)
  31. btn.setBackgroundColor(normalColor, forState: .normal)
  32. btn.setBackgroundColor(disabledColor, forState: .disabled)
  33. return btn
  34. }
  35. //MARK: 1.3、设置背景色
  36. /// 设置背景色
  37. /// - Parameters:
  38. /// - color: 背景色
  39. /// - forState: 状态
  40. func setBackgroundColor(_ color: UIColor, forState: UIControl.State) {
  41. self.setBackgroundImage(backgroundImage(color), for: forState)
  42. }
  43. private func backgroundImage(_ color: UIColor) -> UIImage? {
  44. UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
  45. UIGraphicsGetCurrentContext()?.setFillColor(color.cgColor)
  46. UIGraphicsGetCurrentContext()?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
  47. let colorImage = UIGraphicsGetImageFromCurrentImageContext()
  48. UIGraphicsEndImageContext()
  49. return colorImage
  50. }
  51. }
  52. // MARK: - 二、链式调用
  53. public extension UIButton {
  54. // MARK: 2.1、设置title
  55. /// 设置title
  56. /// - Parameters:
  57. /// - text: 文字
  58. /// - state: 状态
  59. /// - Returns: 返回自身
  60. @discardableResult
  61. func title(_ text: String, _ state: UIControl.State = .normal) -> Self {
  62. setTitle(text, for: state)
  63. return self
  64. }
  65. // MARK: 2.2、设置文字颜色
  66. /// 设置文字颜色
  67. /// - Parameters:
  68. /// - color: 文字颜色
  69. /// - state: 状态
  70. /// - Returns: 返回自身
  71. @discardableResult
  72. func textColor(_ color: UIColor, _ state: UIControl.State = .normal) -> Self {
  73. setTitleColor(color, for: state)
  74. return self
  75. }
  76. // MARK: 2.3、设置字体大小(UIFont)
  77. /// 设置字体大小
  78. /// - Parameter font: 字体 UIFont
  79. /// - Returns: 返回自身
  80. @discardableResult
  81. func font(_ font: UIFont) -> Self {
  82. titleLabel?.font = font
  83. return self
  84. }
  85. // MARK: 2.4、设置字体大小(CGFloat)
  86. /// 设置字体大小(CGFloat)
  87. /// - Parameter fontSize: 字体的大小
  88. /// - Returns: 返回自身
  89. @discardableResult
  90. func font(_ fontSize: CGFloat) -> Self {
  91. titleLabel?.font = UIFont.textF(fontSize)
  92. return self
  93. }
  94. // MARK: 2.5、设置字体中等粗(CGFloat)
  95. /// 设置字体大小(CGFloat)
  96. /// - Parameter fontSize: 字体的大小
  97. /// - Returns: 返回自身
  98. @discardableResult
  99. func mediumFont(_ fontSize: CGFloat) -> Self {
  100. titleLabel?.font = UIFont.textM(fontSize)
  101. return self
  102. }
  103. // MARK: 2.6、设置字体粗体
  104. /// 设置粗体
  105. /// - Parameter fontSize: 设置字体粗体
  106. /// - Returns: 返回自身
  107. @discardableResult
  108. func boldFont(_ fontSize: CGFloat) -> Self {
  109. titleLabel?.font = UIFont.textB(fontSize)
  110. return self
  111. }
  112. // MARK: 2.7、设置图片
  113. /// 设置图片
  114. /// - Parameters:
  115. /// - image: 图片
  116. /// - state: 状态
  117. /// - Returns: 返回自身
  118. @discardableResult
  119. func image(_ image: UIImage?, _ state: UIControl.State = .normal) -> Self {
  120. setImage(image, for: state)
  121. return self
  122. }
  123. }
  124. // MARK: - UIButton 图片 与 title 位置关系
  125. extension UIButton {
  126. /// 图片 和 title 的布局样式
  127. enum ImageTitleLayout {
  128. case imgTop
  129. case imgBottom
  130. case imgLeft
  131. case imgRight
  132. }
  133. // MARK: 3.1、设置图片和 title 的位置关系(提示:title和image要在设置布局关系之前设置)
  134. /// 设置图片和 title 的位置关系(提示:title和image要在设置布局关系之前设置)
  135. /// - Parameters:
  136. /// - layout: 布局
  137. /// - spacing: 间距
  138. /// - Returns: 返回自身
  139. @discardableResult
  140. func setImageTitleLayout(_ layout: ImageTitleLayout, spacing: CGFloat = 0) -> UIButton {
  141. switch layout {
  142. case .imgLeft:
  143. alignHorizontal(spacing: spacing, imageFirst: true)
  144. case .imgRight:
  145. alignHorizontal(spacing: spacing, imageFirst: false)
  146. case .imgTop:
  147. alignVertical(spacing: spacing, imageTop: true)
  148. case .imgBottom:
  149. alignVertical(spacing: spacing, imageTop: false)
  150. }
  151. return self
  152. }
  153. /// 水平方向
  154. /// - Parameters:
  155. /// - spacing: 间距
  156. /// - imageFirst: 图片是否优先
  157. private func alignHorizontal(spacing: CGFloat, imageFirst: Bool) {
  158. let edgeOffset = spacing / 2
  159. imageEdgeInsets = UIEdgeInsets(top: 0, left: -edgeOffset,
  160. bottom: 0,right: edgeOffset)
  161. titleEdgeInsets = UIEdgeInsets(top: 0, left: edgeOffset,
  162. bottom: 0, right: -edgeOffset)
  163. if !imageFirst {
  164. transform = CGAffineTransform(scaleX: -1, y: 1)
  165. imageView?.transform = CGAffineTransform(scaleX: -1, y: 1)
  166. titleLabel?.transform = CGAffineTransform(scaleX: -1, y: 1)
  167. }
  168. contentEdgeInsets = UIEdgeInsets(top: 0, left: edgeOffset, bottom: 0, right: edgeOffset)
  169. }
  170. /// 垂直方向
  171. /// - Parameters:
  172. /// - spacing: 间距
  173. /// - imageTop: 图片是不是在顶部
  174. private func alignVertical(spacing: CGFloat, imageTop: Bool) {
  175. guard let imageWidth = self.imageView?.qsl_width,
  176. let imageHeight = self.imageView?.qsl_height,
  177. let text = self.titleLabel?.text,
  178. let font = self.titleLabel?.font
  179. else {
  180. return
  181. }
  182. let labelString = NSString(string: text)
  183. let titleSize = labelString.size(withAttributes: [NSAttributedString.Key.font: font])
  184. let titleHeight = titleSize.height
  185. let titleWidth = titleSize.width
  186. let insetAmount = spacing / 2
  187. if imageTop {
  188. imageEdgeInsets = UIEdgeInsets(top: -titleHeight - insetAmount,
  189. left: (self.qsl_width - imageWidth) / 2,
  190. bottom: 0,
  191. right: (self.qsl_width - imageWidth) / 2 - titleWidth)
  192. titleEdgeInsets = UIEdgeInsets(top: 0,
  193. left: -imageWidth,
  194. bottom: -imageWidth - insetAmount,
  195. right: 0)
  196. } else {
  197. imageEdgeInsets = UIEdgeInsets(top: 0,
  198. left: (self.qsl_width - imageWidth) / 2,
  199. bottom: -titleHeight - insetAmount,
  200. right: (self.qsl_width - imageWidth) / 2 - titleWidth)
  201. titleEdgeInsets = UIEdgeInsets(top: -imageHeight - insetAmount,
  202. left: -imageWidth,
  203. bottom: 0,
  204. right: 0)
  205. }
  206. }
  207. }
  208. extension UIButton {
  209. //倒计时
  210. func countDown(_ timeOut: Int){
  211. //倒计时时间
  212. var timeout = timeOut
  213. let queue:DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
  214. let _timer:DispatchSource = DispatchSource.makeTimerSource(flags: [], queue: queue) as! DispatchSource
  215. _timer.schedule(wallDeadline: DispatchWallTime.now(), repeating: .seconds(1))
  216. //每秒执行
  217. _timer.setEventHandler(handler: { () -> Void in
  218. if(timeout<=0){ //倒计时结束,关闭
  219. _timer.cancel();
  220. DispatchQueue.main.sync(execute: { () -> Void in
  221. self.setTitle("重新发送", for: .normal)
  222. self.isEnabled = true
  223. })
  224. }else{//正在倒计时
  225. let seconds = timeout
  226. DispatchQueue.main.sync(execute: { () -> Void in
  227. let str = String(describing: seconds)
  228. self.setTitle("\(str)s", for: .normal)
  229. self.isEnabled = false
  230. })
  231. timeout -= 1;
  232. }
  233. })
  234. _timer.resume()
  235. }
  236. }
  237. // MARK: - 六、Button扩大点击事件
  238. private var UIButtonExpandSizeKey = UnsafeRawPointer("UIButtonExpandSizeKey".withCString { $0 })
  239. public extension UIButton {
  240. override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
  241. if self.touchExtendInset == .zero || isHidden || !isEnabled {
  242. return super.point(inside: point, with: event)
  243. }
  244. var hitFrame = bounds.inset(by: self.touchExtendInset)
  245. hitFrame.size.width = max(hitFrame.size.width, 0)
  246. hitFrame.size.height = max(hitFrame.size.height, 0)
  247. return hitFrame.contains(point)
  248. }
  249. }
  250. public extension UIButton {
  251. // MARK: 6.1、扩大UIButton的点击区域,向四周扩展10像素的点击范围
  252. /// 扩大按钮点击区域 如UIEdgeInsets(top: -50, left: -50, bottom: -50, right: -50)将点击区域上下左右各扩充50
  253. ///
  254. /// 提示:theView 扩展点击相应区域时,其扩展的区域不能超过 superView 的 frame ,否则不会相应改点击事件;如果需要响应点击事件,需要对其 superView 进行和 theView 进行同样的处理
  255. var touchExtendInset: UIEdgeInsets {
  256. get {
  257. if let value = objc_getAssociatedObject(self, &UIButtonExpandSizeKey) {
  258. var edgeInsets: UIEdgeInsets = UIEdgeInsets.zero
  259. (value as AnyObject).getValue(&edgeInsets)
  260. return edgeInsets
  261. } else {
  262. return UIEdgeInsets.zero
  263. }
  264. }
  265. set {
  266. objc_setAssociatedObject(self, &UIButtonExpandSizeKey, NSValue(uiEdgeInsets: newValue), .OBJC_ASSOCIATION_COPY_NONATOMIC)
  267. }
  268. }
  269. }