利用runtime解决button重复响应事件

该方法灵感来自于简书《[iOS]利用runtime,解决多次点击相同button,导致重复跳转的问题》

场景

1.当app有点卡的时候,多次点击相同的button,经常出现,跳转了N次相同的界面。

2.当网络较差时,多次点击相同按钮,会造成数据的重复提交或请求。

3....

解决办法

用运行时和分类,替换UIControl响应事件,根据响应的间隔时间来判断是否执行事件。

详细步骤

1.创建一个 UIControl的分类

2.为了方便不同需求对时间间隔的时间要求,在UIControl+Custom.h文件中开放间隔时间属性,UIControl+Custom.h文件的代码为:

#import <UIKit/UIKit.h>

@interface UIControl (Custom)
@property (nonatomic, assign) NSTimeInterval custom_acceptEventInterval;// 可以用这个给重复点击加间隔
@property (nonatomic,strong) void (^callBack)();//在不响应时可以提示用户点击过于频繁等操作
@end

3.在 UIControl+Custom.m文件中实现方法交换(妥善的做法是:先添加方法,如果方法已经存在,就替换原方法),在 UIControl+Custom.m文件的代码为:

#import "UIControl+Custom.h"
#import <objc/runtime.h>
@interface UIControl()
@property (nonatomic, assign) NSTimeInterval custom_acceptEventTime;
@end

@implementation UIControl (Custom)
+ (void)load{
    //使用dispatch_once防止多次替换
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    Method systemMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    SEL sysSEL = @selector(sendAction:to:forEvent:);
    Method customMethod = class_getInstanceMethod(self, @selector(custom_sendAction:to:forEvent:));
    SEL customSEL = @selector(custom_sendAction:to:forEvent:);
    
    //添加方法 语法:BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) 若添加成功则返回No
    // cls:被添加方法的类  name:被添加方法方法名  imp:被添加方法的实现函数  types:被添加方法的实现函数的返回值类型和参数类型的字符串
    BOOL isAddMethod = class_addMethod(self, sysSEL, method_getImplementation(customMethod), method_getTypeEncoding(customMethod));
    
    //如果系统中该方法已经存在了,则替换系统的方法  语法:IMP class_replaceMethod(Class cls, SEL name, IMP imp,const char *types)
    if (isAddMethod) {
        class_replaceMethod(self, customSEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
    }else{
        method_exchangeImplementations(systemMethod, customMethod);
    }
    });
}
//  属性的关联
- (NSTimeInterval )custom_acceptEventInterval{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventInterval") doubleValue];
}

- (void)setCustom_acceptEventInterval:(NSTimeInterval)custom_acceptEventInterval{
    objc_setAssociatedObject(self, "UIControl_acceptEventInterval", @(custom_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void (^)())callBack
{
    return  objc_getAssociatedObject(self, "callBack");
}
- (void)setCallBack:(void (^)())callBack
{
    objc_setAssociatedObject(self, "callBack", callBack, OBJC_ASSOCIATION_RETAIN);
}

- (NSTimeInterval )custom_acceptEventTime{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventTime") doubleValue];
}

- (void)setCustom_acceptEventTime:(NSTimeInterval)custom_acceptEventTime{
    objc_setAssociatedObject(self, "UIControl_acceptEventTime", @(custom_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
    
    // 如果想要设置统一的间隔时间,可以在此处加上以下几句
    // if (self.custom_acceptEventInterval <= 0) {
    //     // 如果没有自定义时间间隔,则默认为2秒
    //    self.custom_acceptEventInterval = 2;
    // }
    // 是否小于设定的时间间隔
    BOOL needSendAction = (NSDate.date.timeIntervalSince1970 - self.custom_acceptEventTime >= self.custom_acceptEventInterval);
        // 更新上一次点击时间戳
    if (self.custom_acceptEventInterval > 0) {
        self.custom_acceptEventTime = NSDate.date.timeIntervalSince1970;
    }
    // 两次点击的时间间隔小于设定的时间间隔时,才执行响应事件
    if (needSendAction) {
        [self custom_sendAction:action to:target forEvent:event];
    }
    else
    {
    //  在不响应自定义方法时的操作
       if (self.callBack)
        {
           self.callBack();
        }
}
    
}
@end

在ViewController中的调用

- (void)viewDidLoad {
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(80, 120, 100, 60);
    [btn setBackgroundColor:[UIColor redColor]];
    [btn setTitle:@"按钮" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(btnPressed) forControlEvents:UIControlEventTouchUpInside];
    btn.custom_acceptEventInterval = 1;
    btn.callBack = ^(){
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"点击过于频繁" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
        [alertController addAction:cancelAction];
        [alertController addAction:okAction];
       [self presentViewController:alertController animated:YES completion:nil];
    };
    [self.view addSubview:btn];
}
- (void)btnPressed
{
    NSLog(@"点击按钮");
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值