- 每次在點(diǎn)擊時(shí)先取消之前的操作
- (void)buttonClick:(UIButton *)violenceBtn{
//點(diǎn)擊按鈕后先取消之前的操作庸娱,再進(jìn)行需要進(jìn)行的操作
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(handleClickEvent:) object:violenceBtn];
[self performSelector:@selector(handleClickEvent:) withObject:violenceBtn afterDelay:0.5f];//時(shí)間0.5
}
//點(diǎn)擊事件的處理方法
- (void)handleClickEvent:(UIButton *)btn{
NSLog(@" %s ",__FUNCTION__);
}
- 點(diǎn)擊后設(shè)為不可被點(diǎn)擊的狀態(tài),幾秒后恢復(fù)
-(void)buttonClick:(UIButton*)violenceBtn{
violenceBtn.enabled =NO;
NSLog(@" %s ",__FUNCTION__);
[self performSelector:@selector(changeButtonStatus) withObject:nil afterDelay:1.0f];//防止重復(fù)點(diǎn)擊
}
-(void)changeButtonStatus{
_btn.enabled =YES;
}
- 通過(guò)點(diǎn)擊間隔時(shí)間防止暴力點(diǎn)擊
-(void)buttonClick:(UIButton*)violenceBtn{
static NSTimeInterval time = 0.0;
NSTimeInterval currentTime = [NSDate date].timeIntervalSince1970;//防止暴力點(diǎn)擊 兩秒內(nèi)只能點(diǎn)擊一次
if (currentTime - time > 2) {//限制用戶(hù)點(diǎn)擊按鈕的時(shí)間間隔大于2秒鐘
NSLog(@" %s ",__FUNCTION__);
}
time = currentTime;
}
- 使用runtime,把按鈕的點(diǎn)擊方法進(jìn)行替換來(lái)設(shè)置時(shí)間間隔谐算。
/**
防止按鈕重復(fù)暴力點(diǎn)擊
*/
@interface UIButton (ViolenceBtn)
//點(diǎn)擊間隔
@property (nonatomic, assign) NSTimeInterval timeInterval;
//用于設(shè)置單個(gè)按鈕不需要被hook (設(shè)為yes說(shuō)明可以重復(fù)點(diǎn)擊)
@property (nonatomic, assign) BOOL isIgnore;
@end
#import "UIButton+ViolenceBtn.h"
#import <objc/runtime.h>
//默認(rèn)時(shí)間間隔
#define defaultInterval 1
@implementation UIButton (ViolenceBtn)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL orginalSel = @selector(sendAction:to:forEvent:);
SEL swizzledSel = @selector(sure_SendAction:to:forEvent:);
//原有方法
Method originalMethod = class_getInstanceMethod(class, orginalSel);
//替換原有方法的新方法
Method swizzledMethod = class_getInstanceMethod(class, swizzledSel);
//先嘗試給源SEL添加IMP熟尉,這里是為了避免源SEL沒(méi)有實(shí)現(xiàn)IMP的情況
//將methodB的實(shí)現(xiàn)添加到系統(tǒng)方法中也就是說(shuō)將methodA方法指針添加成方法methodB的返回值表示是否添加成功
BOOL didAddMethod = class_addMethod(class, orginalSel,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//添加成功了說(shuō)明本類(lèi)中不存在methodB所以此時(shí)必須將方法b的實(shí)現(xiàn)指針換成方法A的,否則b方法將沒(méi)有實(shí)現(xiàn)洲脂。
if (didAddMethod) { //添加成功:說(shuō)明源SEL沒(méi)有實(shí)現(xiàn)IMP斤儿,將源SEL的IMP替換到交換SEL的IMP
class_replaceMethod(class, swizzledSel,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else { //添加失敗:說(shuō)明源SEL已經(jīng)有IMP恐锦,直接將兩個(gè)SEL的IMP交換即可
//添加失敗了說(shuō)明本類(lèi)中有methodB的實(shí)現(xiàn)往果,此時(shí)只需要將methodA和methodB的IMP互換一下即可。
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (NSTimeInterval)timeInterval {
return [objc_getAssociatedObject(self, _cmd) doubleValue];
}
- (void)setTimeInterval:(NSTimeInterval)timeInterval {
objc_setAssociatedObject(self, @selector(timeInterval), @(timeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//當(dāng)按鈕點(diǎn)擊事件sendAction 時(shí)將會(huì)執(zhí)行sure_SendAction
- (void)sure_SendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
if (self.isIgnore) {
//不需要被hook
[self sure_SendAction:action to:target forEvent:event];
return;
}
if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) {
self.timeInterval = self.timeInterval == 0 ? defaultInterval : self.timeInterval;
if (self.isIgnoreEvent) {
return;
} else if (self.timeInterval > 0) {
[self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval];
}
}
//此處 methodA和methodB方法IMP互換了一铅,實(shí)際上執(zhí)行 sendAction陕贮;所以不會(huì)死循環(huán)
self.isIgnoreEvent = YES;
[self sure_SendAction:action to:target forEvent:event];
}
//runtime 動(dòng)態(tài)綁定 屬性
- (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent {
// 注意BOOL類(lèi)型 需要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用錯(cuò),否則set方法會(huì)賦值出錯(cuò)
objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isIgnoreEvent {
//_cmd == @select(isIgnore); 和set方法里一致
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setIsIgnore:(BOOL)isIgnore {
// 注意BOOL類(lèi)型 需要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用錯(cuò)潘飘,否則set方法會(huì)賦值出錯(cuò)
objc_setAssociatedObject(self, @selector(isIgnore), @(isIgnore), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isIgnore{
//_cmd == @select(isIgnore); 和set方法里一致
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)resetState{
[self setIsIgnoreEvent:NO];
}
@end
時(shí)間間隔
目前來(lái)說(shuō)通過(guò)runtime最方便肮之,但是如果是不同按鈕同時(shí)點(diǎn)擊情況還會(huì)發(fā)生,所以可以使用最合適的方法卜录。