iOS开发 - 给UIButton增加Touch事件时间间隔,防止连续触发点击事件造成数据重复提交

之前写的一个防止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

\color{gray}{欢迎大佬来指正纠错,共同学习}

你可能感兴趣的:(iOS开发 - 给UIButton增加Touch事件时间间隔,防止连续触发点击事件造成数据重复提交)