之前写的一个防止UIbutton点击事件连续触发的方法有局限性,重新做了优化在此也感谢 https://www.jianshu.com/p/b8dfd3ef3c5f 提供的参考方案,直接上代码了
UIButton+LRSReTouchExt.h
#import
NS_ASSUME_NONNULL_BEGIN
@interface UIButton (LRSReTouchExt)
//开启UIButton防连击事件控制
- (void)enableControlRepeatTouch;
//关闭UIButton防连击事件控制
- (void)disableControlRepeatTouch;
//设置防连续点击最小时间差(s),不设置则默认值是0.5s
- (void)setAllowRepeatTouchMinTimeInterval:(NSTimeInterval)interval;
@end
NS_ASSUME_NONNULL_END
UIButton+LRSReTouchExt.m
#import "UIButton+LRSReTouchExt.h"
#import
//最小时间差
static NSTimeInterval repeatTouchMinTimeInterval = 0.5;
//原生sendAction:to:forEvent:实现
static void (*originalImplementation)(id, SEL, SEL, id, UIEvent *) = NULL;
//替换的sendAction:to:forEvent:实现
static void replacedImplementation(id object, SEL selector, SEL action, id target, UIEvent *event);
@implementation UIButton (LRSReTouchExt)
//开启UIButton防连击事件控制
- (void)enableControlRepeatTouch {
//获取当前"@selector(sendAction:to:forEvent:)"对应的Method
Method methodNow = class_getInstanceMethod(self.class, @selector(sendAction:to:forEvent:));
//得到当前sendAction:to:forEvent:实现地址
IMP implementationNow = method_getImplementation(methodNow);
//这个实现地址已经是replacedImplementation,说明已经替换过了
if (implementationNow == (IMP)replacedImplementation) {
return;
}
//保存原生的sendAction:to:forEvent:实现地址
originalImplementation = (void (*)(id, SEL, SEL, id, UIEvent *))implementationNow;
const char *type = method_getTypeEncoding(methodNow);
//将实现替换为replacedImplementation
class_replaceMethod(self.class, @selector(sendAction:to:forEvent:), (IMP)replacedImplementation, type);
}
//关闭UIButton防连击事件控制
- (void)disableControlRepeatTouch {
IMP implementationNow = class_getMethodImplementation(self.class, @selector(sendAction:to:forEvent:));
if (originalImplementation && implementationNow == (IMP)replacedImplementation) {
class_replaceMethod(self.class, @selector(sendAction:to:forEvent:), (IMP)originalImplementation, NULL);
}
}
//设置防连续点击最小时间差(s),不设置则默认值是0.5s
- (void)setAllowRepeatTouchMinTimeInterval:(NSTimeInterval)interval {
repeatTouchMinTimeInterval = interval;
}
- (NSTimeInterval)lastTouchTimestamp {
return [objc_getAssociatedObject(self, @selector(lastTouchTimestamp)) doubleValue];
}
- (void)setLastTouchTimestamp:(NSTimeInterval)timestamp {
objc_setAssociatedObject(self, @selector(lastTouchTimestamp), @(timestamp), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
//替换的sendAction:to:forEvent:实现
static void replacedImplementation(id object, SEL selector, SEL action, id target, UIEvent *event) {
//是按钮,并且是UIEventTypeTouches事件,才进行时间戳判断
//但是要排除这两种按钮“CUShutterButton”和"CAMShutterButton",这两个分别是8系统,10系统上相机拍照按钮的类名.这是两个特殊封装过的按钮,如果把它们的事件也用时间戳给过滤掉了,你就会发现app里弹出相机后,要长按才能拍照。
if ([object isKindOfClass:UIButton.self] && ![NSStringFromClass([object class]) isEqualToString:@"CUShutterButton"] && ![NSStringFromClass([object class]) isEqualToString:@"CAMShutterButton"] && event.type == UIEventTypeTouches) {
//进行时间戳判断
UIButton *button = (UIButton *)object;
if (ABS(event.timestamp - button.lastTouchTimestamp) < repeatTouchMinTimeInterval) {
//时间过短,就此返回,此次事件Send也中止
return;
}
button.lastTouchTimestamp = event.timestamp;
}
//时间戳上没问题,不属于快速点击
if (originalImplementation) {
//调用系统原生实现,继续完成事件的Send
originalImplementation(object, selector, action, target, event);
}
}
调用方法(在需要的地方手动开启)
- (void)awakeFromNib{
[super awakeFromNib];
///添加事件延迟时间(在需要的地方手动开启)
[self.handle_btn enableControlRepeatTouch];
[self.handle_btn setAllowRepeatTouchMinTimeInterval:1.f];
[self.handle_btn addTarget:self action:@selector(handleBtnClickAction:) forControlEvents:UIControlEventTouchUpInside];
}
同样的根据需要可以将方法扩展成类方法在 AppDelegate 中统一调用开启/关闭
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions