iOS开发:Runtime解决UIButton重复点击

Posted wuwuFQ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发:Runtime解决UIButton重复点击相关的知识,希望对你有一定的参考价值。

这个用到的太多了,网上的博客也很多,但是错的博客也很多。今天给大家介绍一个正确的。Show(@^_^@)

错误案例 ❎

这里就不贴错误代码了,怕误导大家,说一下大概的思想。网上流行两种做法:

UIControl的子类包括:UIButton、UIDatePicker、UIPageControl、UISegmentedControl、UISlider、UIStepper、UISwitch。

  • 第一种做法:创建UIButton分类,自定义fq_sendAction方法,和系统的 sendAction:to:forEvent: 交换IMP。
    • 弊端:UIButton是没有sendAction:to:forEvent:方法的,直接交换IMP其实是和UIButton的父类,也就是UIControl交换了方法实现。那么当你使用UISwitch等UIControl的其他子类的时候,就会因为找不到fq_sendAction方法而崩溃。
  • 第二种做法:创建UIControl分类,自定义fq_sendAction方法,和系统的 sendAction:to:forEvent: 交换IMP。
    • 弊端:这样做可以避免UIControl的子类调用崩溃,但是所有UIControl的子类都会走fq_sendAction方法,会导致事件不连续。

正确姿势✅

  1. 创建UIButton分类

.h文件

#import <UIKit/UIKit.h>

@interface UIButton (FQTapEvent)
@property (nonatomic, assign) BOOL ignoreEvent; // 是否忽略点击

@end

.m文件

#import "UIButton+FQTapEvent.h"
#import <objc/runtime.h>
#define EVENTINTERVAL 0.5 // 间隔时间

@implementation UIButton (FQTapEvent)
+ (void)load
       Method sendEvent = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method my_sendEvent = class_getInstanceMethod(self, @selector(fq_sendAction:to:forEvent:));
    BOOL addSuccess = class_addMethod(self, @selector(sendAction:to:forEvent:), method_getImplementation(sendEvent), method_getTypeEncoding(sendEvent));
    if (addSuccess) 
        sendEvent = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    
    // 交换方法
    method_exchangeImplementations(sendEvent, my_sendEvent);


- (void)fq_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
    if (!self.ignoreEvent) 
        self.ignoreEvent = YES;
        [self fq_sendAction:action to:target forEvent:event];
        [self performSelector:@selector(setIgnoreEvent:) withObject:@(NO) afterDelay:EVENTINTERVAL];
    


- (void)setIgnoreEvent:(BOOL)ignoreEvent
    objc_setAssociatedObject(self, @selector(ignoreEvent), @(ignoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);


- (BOOL)ignoreEvent
    return [objc_getAssociatedObject(self, @selector(ignoreEvent)) boolValue];

@end


我们分析一下load里面的代码实现

+ (void)load
    //1.
    Method sendEvent = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    //2.
    Method my_sendEvent = class_getInstanceMethod(self, @selector(fq_sendAction:to:forEvent:));
    //3.
    BOOL addSuccess = class_addMethod(self, @selector(sendAction:to:forEvent:), method_getImplementation(sendEvent), method_getTypeEncoding(sendEvent));
    if (addSuccess) 
    //4.
        sendEvent = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    
    // 5.交换方法
    method_exchangeImplementations(sendEvent, my_sendEvent);


  1. 获取系统方法 sendAction:to:forEvent:Method信息 赋值给 sendEvent
  2. 获取自定义方法 fq_sendAction:to:forEvent:Method信息 赋值给 my_sendEvent
  3. 使用 class_addMethod 给UIButton添加系统的 sendAction:to:forEvent:
  4. 添加成功说明UIButton没有这个方法的实现,那么 sendEvent 里面的class信息其实是父类UIControl的,这个时候我们再 class_getInstanceMethod 获取一遍就是 UIButtonMethod信息
  5. 交换两个方法的IMP

这样才算正确解决了UIButton重复点击问题,其他的子类也不受影响,Nice!! Done!!

以上是关于iOS开发:Runtime解决UIButton重复点击的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发:Runtime解决UIButton重复点击

iOS开发:Runtime解决UITapGesture重复点击问题

iOS开发:Runtime解决UITapGesture重复点击问题

iOS开发:Runtime常见方法

iOS开发:Runtime常见方法

iOS开发:Runtime常见方法