偶然间看到了IOS 防止UIButton 重复点击这边文章,兴趣的试了里面Swift关于runtime的延迟方法,由于语言版本的升级 initialize() 这个无法使用,试了几种办法都没解决,最后找度娘和G神解决下问题。发现了Swift3.x 继续 Method Swizzling。好吧、问题迎刃而解。
记录下,Swift作为新语言还在不断调整修改中、以后还是要多关注一些新特性才行。
下面附带说下修改的代码吧,把文章IOS 防止UIButton 重复点击里面关于 inittialize() 的方法内容替换到Swift3.x 继续 Method Swizzling里面的awake() 方法里面,其他的都保留不变就行了。
如下:
import Foundation
import UIKit
// 定义 protocol
public protocol SelfAware: class {
static func awake()
}
// 创建代理执行单例
class NothingToSeeHere {
static func harmlessFunction() {
var autoreleaseintTypes: AutoreleasingUnsafeMutablePointer<AnyClass>? = nil
let typeCount = Int(objc_getClassList(nil, 0))
let types = UnsafeMutablePointer<AnyClass?>.allocate(capacity: typeCount)
autoreleaseintTypes = AutoreleasingUnsafeMutablePointer<AnyClass>(types)
objc_getClassList(autoreleaseintTypes, Int32(typeCount)) // 获取所有的类
for index in 0 ..< typeCount {
(types[index] as? SelfAware.Type)?.awake() // 如果该类实现了SelfAware协议,那么调用awake方法
}
types.deallocate(capacity: typeCount)
}
}
// 执行单例
extension UIApplication {
private static let runOnce: Void = {
// 使用静态属性以保证只调用一次(该属性是个方法)
NothingToSeeHere.harmlessFunction()
}()
override open var next: UIResponder? {
UIApplication.runOnce
return super.next
}
}
// 将类设置为代理并在代理中实现运行时代码
extension UIButton: SelfAware {
public static func awake() {
let changeBefore: Method = class_getInstanceMethod(self, #selector(UIButton.sendAction(_:to:for:)))!
let changeAfter: Method = class_getInstanceMethod(self, #selector(UIButton.cs_sendAction(action:to:for:)))!
method_exchangeImplementations(changeBefore, changeAfter)
}
private struct cs_associatedKeys {
static var accpetEventInterval = "cs_acceptEventInterval"
static var acceptEventTime = "cs_acceptEventTime"
}
/*
重复点击的时间间隔--自己手动随意设置
利用运行机制 将 accpetEventInterval 值修改
*/
var cs_accpetEventInterval: TimeInterval {
get {
if let accpetEventInterval = objc_getAssociatedObject(self, &cs_associatedKeys.accpetEventInterval) as? TimeInterval {
return accpetEventInterval
}
return 5
}
set {
objc_setAssociatedObject(self, &cs_associatedKeys.accpetEventInterval, newValue as TimeInterval, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/*
重复点击的时间间隔--自己手动随意设置
利用运行时机制 将 acceptEventTime 值修改
*/
var cs_acceptEventTime: TimeInterval {
get {
if let acceptEventTime = objc_getAssociatedObject(self, &cs_associatedKeys.acceptEventTime) as? TimeInterval {
return acceptEventTime
}
return 5
}
set {
objc_setAssociatedObject(self, &cs_associatedKeys.acceptEventTime, newValue as TimeInterval, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/*
在这个方法中判断接收到当前事件的时间间隔是否满足我们所设定的间隔,会一直循环调用到满足才会return
*/
@objc func cs_sendAction(action: Selector, to target: AnyObject?, for event: UIEvent?) {
if NSDate().timeIntervalSince1970 - self.cs_acceptEventTime < self.cs_accpetEventInterval {
return
}
if self.cs_accpetEventInterval > 0 {
self.cs_acceptEventTime = NSDate().timeIntervalSince1970
}
self.cs_sendAction(action: action, to: target, for: event)
}
}