UIButton various click events encapsulation, event response area modification, text image position (vertical center, horizontal center)
import Foundation
import UIKit
// MARK: - Click event closure and expand click range
typealias BtnAction = (UIButton) - > ()extension UIButton{
private struct AssociatedKeys{
static var actionKey = "actionKey"
}
@objc dynamic var action: BtnAction? {
set{
objc_setAssociatedObject(self.&AssociatedKeys.actionKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY)}get{
if let action = objc_getAssociatedObject(self.&AssociatedKeys.actionKey) as? BtnAction{
return action
}
return nil}}/// Add a click event
/// -parameter action: closure to execute when clicked
@discardableResult // Remove the warning when the return value is not used
func addClickAction(action:@escaping BtnAction) -> UIButton {
return self.addEvent(event: .touchUpInside, action: action)
}
/// Add an event
/// - Parameters:
/// - event: indicates the added event
/// -action: closure to execute in event response
@discardableResult // Remove the warning when the return value is not used
func addEvent(event:UIControl.Event.action:@escaping BtnAction ) -> UIButton{
self.action = action
self.addTarget(self, action: #selector(touchUpInSideBtnAction), for: event)
return self
}
@objc func touchUpInSideBtnAction(btn: UIButton) {
if let action = self.action {
action(btn)
}
}
@discardableResult // Remove the warning when the return value is not used
func addCountdown(time:Int.endAction:@escaping() - > ()) -> UIButton {
// Countdown time
var timeout = time
let queue:DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
let _timer:DispatchSource = DispatchSource.makeTimerSource(flags: [], queue: queue) as! DispatchSource
_timer.schedule(wallDeadline: DispatchWallTime.now(), repeating: .seconds(1))
// execute every second
_timer.setEventHandler(handler: { () -> Void in
if(timeout< =0) {// The timer is closed
_timer.cancel();
DispatchQueue.main.sync(execute: { () -> Void in
self.isEnabled = true
endAction()
})
}else{// Counting down
let seconds = timeout
DispatchQueue.main.sync(execute: { () -> Void in
let str = String(describing: seconds)
let s = "Seconds"
self.titleLabel?.text = "\(str)\(s)"
self.setTitle("\(str)\(s)", for: .normal)
self.isEnabled = false
})
timeout - = 1;
}
})
_timer.resume()
return self
}
//
private struct RuntimeKey {
static let clickEdgeInsets = UnsafeRawPointer.init(bitPattern: "clickEdgeInsets".hashValue)
/ / /... Other Key statements
}
/// Need to expand the click margin
public var addClickEdgeInsets: UIEdgeInsets? {
set {
objc_setAssociatedObject(self.UIButton.RuntimeKey.clickEdgeInsets!, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY)}get {
return objc_getAssociatedObject(self.UIButton.RuntimeKey.clickEdgeInsets!) as? UIEdgeInsets ?? UIEdgeInsets.zero
}
}
// Rewrite the system method to modify the click area
open override func point(inside point: CGPoint.with event: UIEvent?). -> Bool {
super.point(inside: point, with: event)
var bounds = self.bounds
if (addClickEdgeInsets ! = nil) {
let x: CGFloat = -(addClickEdgeInsets?.left ?? 0)
let y: CGFloat = -(addClickEdgeInsets?.top ?? 0)
let width: CGFloat = bounds.width + (addClickEdgeInsets?.left ?? 0) + (addClickEdgeInsets?.right ?? 0)
let height: CGFloat = bounds.height + (addClickEdgeInsets?.top ?? 0) + (addClickEdgeInsets?.bottom ?? 0)
bounds = CGRect(x: x, y: y, width: width, height: height) // A negative value is the method response range
}
return bounds.contains(point)
}
}
// MARK: - Image text position
extension UIButton{
enum RGButtonImagePosition {
case top // Picture on top, text on bottom, vertically centered
case bottom // Image at bottom, text at top, vertically centered
case left // Image on the left, text on the right, horizontally centered
case right // Image on the right, text on the left, horizontally centered
}
/// -description sets the position of the Button image
/// - Parameters:
/// - style
/// -spacing: Spacing between button images and text
func imagePosition(style: RGButtonImagePosition.spacing: CGFloat) {
// Get the width and height of the imageView and titleLabel
let imageWidth = self.imageView?.frame.size.width
let imageHeight = self.imageView?.frame.size.height
var labelWidth: CGFloat! = 0.0
var labelHeight: CGFloat! = 0.0
labelWidth = self.titleLabel?.intrinsicContentSize.width
labelHeight = self.titleLabel?.intrinsicContentSize.height
// Initialize imageEdgeInsets and labelEdgeInsets
var imageEdgeInsets = UIEdgeInsets.zero
var labelEdgeInsets = UIEdgeInsets.zero
// Get imageEdgeInsets and labelEdgeInsets based on style and space
switch style {
case .top:
// Up, left, down, right
imageEdgeInsets = UIEdgeInsets(top: -labelHeight-spacing/2, left: 0, bottom: 0, right: -labelWidth)
labelEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth!, bottom: -imageHeight! -spacing/2, right: 0)
break;
case .left:
imageEdgeInsets = UIEdgeInsets(top: 0, left: -spacing/2, bottom: 0, right: spacing)
labelEdgeInsets = UIEdgeInsets(top: 0, left: spacing/2, bottom: 0, right: -spacing/2)
break;
case .bottom:
imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: -labelHeight! -spacing/2, right: -labelWidth)
labelEdgeInsets = UIEdgeInsets(top: -imageHeight! -spacing/2, left: -imageWidth!, bottom: 0, right: 0)
break;
case .right:
imageEdgeInsets = UIEdgeInsets(top: 0, left: labelWidth+spacing/2, bottom: 0, right: -labelWidth-spacing/2)
labelEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth! -spacing/2, bottom: 0, right: imageWidth! +spacing/2)
break;
}
self.titleEdgeInsets = labelEdgeInsets
self.imageEdgeInsets = imageEdgeInsets
}
}
// MARK: - Other
extension UIButton {
// create a button based on the text
/// - Parameters:
/// - title: text
/// -font
/// -titlecolor: font color
/// - Returns: button
static func buildBtn(title: String.font: UIFont.titleColor: UIColor) -> UIButton {
let btn = UIButton(frame: .zero)
btn.backgroundColor = .clear
btn.setTitle(title, for: .normal)
btn.setTitleColor(titleColor, for: .normal)
btn.titleLabel?.font = font
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 1000, height: 1000))
label.text = title
label.textColor = titleColor
label.font = font
label.sizeToFit()
// btn.addSubview(label)
btn.width = label.width
btn.height = label.height
return btn
}
}
Copy the code
Demo: Github