iOS学习笔记之UIButton防止连续点击

2019-09-15  本文已影响0人  Kity_Pei

生活就像海洋,只有意志坚强的人,才能到达彼岸。

前言

为什么UIButton要防止连续点击?

应用场景如下所示但不仅仅如此:

由于手机性能等其他原因,用户在点击UIButton进行页面Push操作时,由于误操作连点了,导致Push多个相同页面;
当用户点击UIButton需要请求网络,由于网络请求颇耗时,如果连续点击,意味着需要执行多次相同操作的网络请求,造成服务器资源浪费;
防止暴力点击。

正文

防止UIButton连续点击的方案概述

各个方案的优缺点

方案一
优点:逻辑最清晰,易懂;
缺点:由于属性值是Bool类型的,所以需要成对出现,代码书写的比较分散,容易出现异常。

方案二

优点:逻辑比较清晰;
缺点:连续点击按钮时取消之前的点击事件,从而只执行最后一次点击事件,所以可能出现延迟现象,代码书写也比较分散。

方案三

利用Runtime使用类别来处理UIButton,代码书写比较集中,修改编辑比较方便。

各个方案的代码实现

方案一

@IBAction func onClickedBtn(_ sender: UIButton) {
    sender.isEnabled = false
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(1)) {
        sender.isEnabled = true
    }
}

方案二

@IBAction func onClickedBtn(_ sender: UIButton) {
    NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(onClickedBtn(_:)), object: sender)
    self.perform(#selector(onClickedBtn(_:)), with: sender, afterDelay: 2.0)
}

方案三

public extension UIButton {
    private struct AssociatedKeys {
        static var eventInterval = "eventInterval"
        static var eventUnavailable = "eventUnavailable"
    }

    /// 重复点击的时间 属性设置
    var eventInterval: TimeInterval {
        get {
            if let interval = objc_getAssociatedObject(self, &AssociatedKeys.eventInterval) as? TimeInterval {
                return interval
            }
            return 0.5
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.eventInterval, newValue as TimeInterval, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    /// 按钮不可点 属性设置
    private var eventUnavailable : Bool {
        get {
            if let unavailable = objc_getAssociatedObject(self, &AssociatedKeys.eventUnavailable) as? Bool {
                return unavailable
            }
            return false
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.eventUnavailable, newValue as Bool, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    /// 新建初始化方法,在这个方法中实现在运行时方法替换
    class func initializeMethod() {
        let selector = #selector(UIButton.sendAction(_:to:for:))
        let newSelector = #selector(new_sendAction(_:to:for:))
        
        let method: Method = class_getInstanceMethod(UIButton.self, selector)!
        let newMethod: Method = class_getInstanceMethod(UIButton.self, newSelector)!
        
        if class_addMethod(UIButton.self, selector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)) {
            class_replaceMethod(UIButton.self, newSelector, method_getImplementation(method), method_getTypeEncoding(method))
        } else {
            method_exchangeImplementations(method, newMethod)
        }
    }

    /// 在这个方法中
    @objc private func new_sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
        if eventUnavailable == false {
            eventUnavailable = true
            new_sendAction(action, to: target, for: event)
            // 延时
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + eventInterval, execute: {
                self.eventUnavailable = false
            })
        }
    }
}

注意:
如果是使用方案三的话,由于在Swift 4中的initialize()已经被废弃,所以需要在AppDelegate调用自己写的initializeMethod()方法。

上一篇下一篇

猜你喜欢

热点阅读