场景
当app有点卡的时候,多次点击相同的button,经常出现,跳转了N次相同的界面(比如闲鱼)
解决办法
用运行时和分类,替换UIControl
响应事件,根据响应的间隔时间来判断是否执行事件。
详细步骤
- 创建一个
UIControl
的分类
Snip20160816_3.png
为了方便他人调整不同的间隔时间需求,在UIControl+Custom.h
文件中开放间隔时间属性,UIControl+Custom.h
文件的代码为:
// UIControl+Custom.h
// Created by ocarol on 16/8/16.
// Copyright © 2016年 ocarol. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIControl (Custom)
@property (nonatomic, assign) NSTimeInterval custom_acceptEventInterval;// 可以用这个给重复点击加间隔
@end
在UIControl+Custom.m
文件中实现方法交换(妥善的做法是:先添加方法,如果方法已经存在,就替换原方法),在UIControl+Custom.m
文件的代码为:
// UIControl+Custom.m
// Created by ocarol on 16/8/16.
// Copyright © 2016年 ocarol. All rights reserved.
//
#import "UIControl+custom.h"
#import <objc/runtime.h>
@interface UIControl()
@property (nonatomic, assign) NSTimeInterval custom_acceptEventTime;
@end
@implementation UIControl (Custom)
+ (void)load{
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 didAddMethod = class_addMethod(self, sysSEL, method_getImplementation(customMethod), method_getTypeEncoding(customMethod));
//如果系统中该方法已经存在了,则替换系统的方法 语法:IMP class_replaceMethod(Class cls, SEL name, IMP imp,const char *types)
if (didAddMethod) {
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);
}
- (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{
// 如果想要设置统一的间隔时间,可以在此处加上以下几句
// 值得提醒一下:如果这里设置了统一的时间间隔,会影响UISwitch,如果想统一设置,又不想影响UISwitch,建议将UIControl分类,改成UIButton分类,实现方法是一样的
// 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];
}
}
@end
扩展阅读:
Objective-C Runtime Reference - Apple Developer
Objective C运行时(runtime)技术的几个要点总结
OC运行时编程指南
Method Swizzling
网友评论
当快速点击一个按钮后 等待一会再点击这个按钮,这个按钮可能会不响应
if (self.custom_acceptEventInterval > 0) {
self.custom_acceptEventTime = NSDate.date.timeIntervalSince1970;
}
这一段代码我觉得有问题,这种情况下,一直点击按钮,就一直不会触发按钮的点击事件,我把代码改成以下这样
// 更新上一次点击时间戳
if (needSendAction) {
self.custom_acceptEventTime = NSDate.date.timeIntervalSince1970;
}
不知道原先的代码是不是作者的本意,如果我妄言了,见谅
class_replaceMethod(self, customSEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
} " 这段代码有什么意义,可以直接删掉;
2. 如果button的其他分类中有实现与custom_sendAction:to:forEvent:重名的方法,这个要怎么判断并做过滤呢?