生活就像海洋堪嫂,只有意志堅強的人甚侣,才能到達(dá)彼岸。
前言
為什么UIButton
要防止連續(xù)點擊蔑舞?
應(yīng)用場景如下所示但不僅僅如此:
由于手機性能等其他原因,用戶在點擊UIButton
進(jìn)行頁面Push
操作時嘹屯,由于誤操作連點了攻询,導(dǎo)致Push
多個相同頁面;
當(dāng)用戶點擊UIButton
需要請求網(wǎng)絡(luò)州弟,由于網(wǎng)絡(luò)請求頗耗時钧栖,如果連續(xù)點擊低零,意味著需要執(zhí)行多次相同操作的網(wǎng)絡(luò)請求,造成服務(wù)器資源浪費拯杠;
防止暴力點擊掏婶。
正文
防止UIButton
連續(xù)點擊的方案概述
-
方案一:通過
UIButton
的enabled
和userInteractionEnabled
兩個屬性來控制UIButton
是否可點擊。 -
方案二:通過
NSObject
以下兩個方法來控制UIButton
的響應(yīng)事件事件間隔潭陪。+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector: (SEL)aSelector object:(id)anArgument; - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
-
方案三:通過
Runtime
來控制UIButton
的響應(yīng)事件的時間間隔雄妥。
各個方案的優(yōu)缺點
方案一
優(yōu)點:邏輯最清晰,易懂依溯;
缺點:由于屬性值是Bool
類型的老厌,所以需要成對出現(xiàn),代碼書寫的比較分散黎炉,容易出現(xiàn)異常枝秤。
方案二
優(yōu)點:邏輯比較清晰;
缺點:連續(xù)點擊按鈕時取消之前的點擊事件慷嗜,從而只執(zhí)行最后一次點擊事件淀弹,所以可能出現(xiàn)延遲現(xiàn)象,代碼書寫也比較分散庆械。
方案三
利用Runtime
使用類別來處理UIButton
薇溃,代碼書寫比較集中,修改編輯比較方便干奢。
各個方案的代碼實現(xiàn)
方案一
@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"
}
/// 重復(fù)點擊的時間 屬性設(shè)置
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)
}
}
/// 按鈕不可點 屬性設(shè)置
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)
}
}
/// 新建初始化方法,在這個方法中實現(xiàn)在運行時方法替換
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()已經(jīng)被廢棄,所以需要在AppDelegate
調(diào)用自己寫的initializeMethod()
方法忿峻。