Przeglądaj źródła

添加防止多次点击类别

jsonwang 3 lat temu
rodzic
commit
e17e294998

+ 116 - 0
BFFramework/Classes/BFModules/BFCategorys/BFUIButton+ext.swift

@@ -61,3 +61,119 @@ extension UIButton {
         self.imageEdgeInsets = imageEdgeInsets
     }
 }
+
+/*
+   e.g.
+ let button = UIButton(frame: CGRect(x: 0, y: 300, width: 100, height: 22))
+ button.setTitle("按钮", for: .normal)
+ button.setTitleColor(.black, for: .normal)
+ button.eventInterval = 2.0  //  按的时间间隔, 设置为0的时候就是可以反复点击
+ view.addSubview(button)
+ 
+ */
+// MARK: - 按钮的反复点击问题 交换方法
+extension UIButton {
+
+    /// 对外交换方法的方法 AppDelegate Launch中使用
+    public  static func methodExchange() {
+        DispatchQueue.once(token: "UIButton") {
+            let originalSelector = Selector.sysFunc
+            let swizzledSelector = Selector.myFunc
+            changeMethod(originalSelector, swizzledSelector, self)
+        }
+    }
+    
+    
+    /// Runtime方法交换
+    ///
+    /// - Parameters:
+    ///   - original: 原方法
+    ///   - swizzled: 交换方法
+    ///   - object: 对象
+    public static func changeMethod(_ original: Selector, _ swizzled: Selector, _ object: AnyClass) -> () {
+        
+        guard let originalMethod = class_getInstanceMethod(object, original),
+              let swizzledMethod = class_getInstanceMethod(object, swizzled) else {
+            return
+        }
+        
+        let didAddMethod = class_addMethod(object, original, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
+        if didAddMethod {
+            class_replaceMethod(object, swizzled, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
+        } else {
+            method_exchangeImplementations(originalMethod, swizzledMethod)
+        }
+    }
+    
+    
+    /// 结构体静态key
+    public struct UIButtonKey {
+        static var isEventUnavailableKey = "isEventUnavailableKey"
+        static var eventIntervalKey = "eventIntervalKey"
+    }
+    
+    /// 触发事件的间隔
+    public var eventInterval: TimeInterval {
+        get {
+            return (objc_getAssociatedObject(self, &UIButtonKey.eventIntervalKey) as? TimeInterval) ?? 0
+        }
+        set {
+            objc_setAssociatedObject(self, &UIButtonKey.eventIntervalKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    /// 是否可以触发事件
+    fileprivate var isEventUnavailable: Bool {
+        get {
+            return (objc_getAssociatedObject(self, &UIButtonKey.isEventUnavailableKey) as? Bool) ?? false
+        }
+        set {
+            objc_setAssociatedObject(self, &UIButtonKey.isEventUnavailableKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    /// 手写的set方法
+    ///
+    /// - Parameter isEventUnavailable: 事件是否可用
+    @objc private func setIsEventUnavailable(_ isEventUnavailable: Bool) {
+        self.isEventUnavailable = isEventUnavailable
+    }
+    
+    /// mySendAction
+    @objc fileprivate func mySendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
+        print("交换了按钮事件的方法")
+        
+        if isEventUnavailable == false {
+            isEventUnavailable = true
+            mySendAction(action, to: target, for: event)
+            perform(#selector(setIsEventUnavailable(_: )), with: false, afterDelay: eventInterval)
+        }
+    }
+}
+
+fileprivate extension Selector {
+    static let sysFunc = #selector(UIButton.sendAction(_:to:for:))
+    static let myFunc = #selector(UIButton.mySendAction(_:to:for:))
+}
+
+extension DispatchQueue {
+    private static var onceTracker = [String]()
+    
+    open class func once(token: String, block:() -> ()) {
+        //注意defer作用域,调用顺序——即一个作用域结束,该作用域中的defer语句自下而上调用。
+        objc_sync_enter(self)
+        defer {
+            print("线程锁退出")
+            objc_sync_exit(self)
+        }
+        
+        if onceTracker.contains(token) {
+            return
+        }
+        onceTracker.append(token)
+        block()
+        defer {
+            print("block执行完毕")
+        }
+    }
+}