发现问题
双十一将至,项目上遇到了个问题。用户(QA🙄)连续点击去下单支付的时候,微信/支付宝可能会返回“重复订单”的错误信息(废话你点这么多次能不重复么😂),同理点击按钮多次push的情况也会出现。
解决思路
这个问题主要因为button被多次点击,重复的响应了点击事件。
解决方案
通常是如何解决
一般做法是在按钮点击事件里设置这个按钮不可点击,适当的时间之后再设置回来,或者在点击事件最后设置可以点击。下面是在响应事件的方法内禁止点击的代码。
- (IBAction)clickBtn1:(UIbutton *)sender
{
sender.enabled = NO;
doSomething
sender.enabled = YES;
}
如果想做N秒内禁止点重复击操作则需要用方法performSelector:withObject:afterDelay:
如果想解决程序里所有重复点击的方法,需要把每个按钮都写一遍,这不扯淡么…
漂亮的解决方法
UIbutton
继承自UIControl
,可以给UIControl
添加Category。
MasterDefaultRepeatEventInterval
可以定制时间间隔。
#import <UIKit/UIKit.h>
static NSTimeInterval MasterDefaultRepeatEventInterval = 1.5;
@interface UIControl (Interval)
@property (nonatomic, assign) NSTimeInterval M_repeatEventInterval;
@end
这里用了单例的方法来替换。
#import "UIControl+Interval.h"
#import <objc/runtime.h>
@implementation UIControl (Interval)
- (NSTimeInterval)M_repeatEventInterval {
return [objc_getAssociatedObject(self, _cmd) doubleValue];
}
- (void)setM_repeatEventInterval:(NSTimeInterval)M_repeatEventInterval {
objc_setAssociatedObject(self, @selector(M_repeatEventInterval), @(M_repeatEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalselector = @selector(sendAction:to:forEvent:);
SEL swizzledSelector = @selector(M_sendAction:to:forEvent:);
Method originalMethod = class_getInstanceMethod(class, @selector(sendAction:to:forEvent:));
Method swizzleMethod = class_getInstanceMethod(class, @selector(M_sendAction:to:forEvent:));
//添加方法 语法:BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) 若添加成功则返回No
// cls:被添加方法的类 name:被添加方法方法名 imp:被添加方法的实现函数 types:被添加方法的实现函数的返回值类型和参数类型的字符串
BOOL didAddMethod = class_addMethod(class, originalselector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, swizzleMethod);
}
});
}
- (void)M_sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event {
static BOOL ignoreEvent = NO;
if (self.M_repeatEventInterval > 0) {
if (ignoreEvent) {
return;
}else{
ignoreEvent = YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.M_repeatEventInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
ignoreEvent = NO;
});
[self M_sendAction:action to:target forEvent:event];
}
}else {
[self M_sendAction:action to:target forEvent:event];
}
}
就这样!很快,很棒,很优雅。就这样吧😌