首先理解下幾個概念
1瞎惫、IMP:它是指向一個方法具體實現(xiàn)的指針诀豁,每一個方法都有一個對應(yīng)的IMP伏穆,當(dāng)你發(fā)起一個消息之后,最終它會執(zhí)行的那段代碼恨统,就是由IMP這個函數(shù)指針指向了這個方法實現(xiàn)的
2叁扫、SEL:方法名稱的描述,只記錄方法的編號不記錄具體的方法畜埋,具體的方法是 IMP
3莫绣、Method:是一個類實例,里面的結(jié)構(gòu)體有一個方法選標(biāo) SEL – 表示該方法的名稱悠鞍,一個types – 表示該方法參數(shù)的類型对室,一個 IMP - 指向該方法的具體實現(xiàn)的函數(shù)指針。
針對UIButton咖祭、UISegmentedControl掩宜、UISwitch這些繼承自UIControl的控件可通過hook sendAction:to:forEvent:這個方法實現(xiàn)控件的重復(fù)點(diǎn)擊
具體思路:
1、創(chuàng)建UIButton的類別么翰,使用runtime添加public屬性eventInterval作為計時因子
2牺汤、添加private屬性eventUnavailable來控制button的點(diǎn)擊事件是否有效
3、在+load方法中實現(xiàn)系統(tǒng)sendAction:to:forEvent:方法與自定義方法進(jìn)行交換
代碼實現(xiàn):
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method origMethod = class_getInstanceMethod([self class], @selector(sendAction:to:forEvent:));
SEL origsel = @selector(sendAction:to:forEvent:);
Method swizMethod = class_getInstanceMethod([self class], @selector(tk_sendAction:to:forEvent:));
SEL swizsel = @selector(tk_sendAction:to:forEvent:);
BOOL addMehtod = class_addMethod([self class], origsel, method_getImplementation(swizMethod), method_getTypeEncoding(swizMethod));
if (addMehtod) {
class_replaceMethod([self class], swizsel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
} else {
method_exchangeImplementations(origMethod, swizMethod);
}
});
}
- (void)tk_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
if (self.eventUnavailable == NO) {
self.eventUnavailable = YES;
[self tk_sendAction:action to:target forEvent:event];
[self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.eventInterval];
}
}
針對單擊事件可對UITapGestureRecognizer的initWithTarget:action:或addTarget:action:進(jìn)行hook
具體思路:
1浩嫌、創(chuàng)建UITapGestureRecognizer的類別檐迟,使用runtime添加public屬性eventInterval作為計時因子
2戴已、添加private屬性eventUnavailable來控制button的點(diǎn)擊事件是否有效
3、在+load方法中實現(xiàn)系統(tǒng)initWithTarget:action:方法與自定義方法進(jìn)行交換
4锅减、將target和selector關(guān)聯(lián)到創(chuàng)建的類別中并且將selector替換成類別中自定義的響應(yīng)方法
代碼實現(xiàn):
- (instancetype)initTKWithTarget:(id)target action:(SEL)action {
self = [self initTKWithTarget:self action:@selector(tap:)];
objc_setAssociatedObject(self, gestureTargetKey, target, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, gestureSelKey, NSStringFromSelector(action), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return self;
}
- (void)tap:(UIGestureRecognizer *)tapGesture {
id target = objc_getAssociatedObject(self, gestureTargetKey);
SEL action = NSSelectorFromString(objc_getAssociatedObject(self, gestureSelKey));
if (self.eventUnavailable == NO) {
self.eventUnavailable = YES;
[target performSelector:action];
[self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.eventInterval];
}
}
針對單擊事件的第二種實現(xiàn)方式實現(xiàn)帶參數(shù)的init方法
具體思路:
1糖儡、在分類中實現(xiàn)initWithTarget:action:eventIntervl:的方法通過傳進(jìn)去計時因子控制點(diǎn)擊事件是否可以響應(yīng),將手勢的代理設(shè)置成新建的分類
2怔匣、添加屬性eventUnavailable來控制點(diǎn)擊事件是否有效
3握联、通過重寫gestureRecognizer:shouldReceiveTouch:來控制點(diǎn)擊事件是否響應(yīng)
代碼實現(xiàn):
-(instancetype)initWithTarget:(id)target action:(SEL)action eventIntervl:(NSTimeInterval)eventIntervl {
self = [super init];
if (self) {
self.eventInterval = eventIntervl;
self.delegate = self;
[self addTarget:target action:action];
}
return self;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if (self.eventUnavailable == NO) {
self.eventUnavailable = YES;
[self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.eventInterval];
return YES;
} else {
return NO;
}
}