1. 程式人生 > >利用runtime解決button重複響應事件

利用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(@"點選按鈕");
}