引入
在项目中经常能用到一个功能,就是对于按钮的点击时间间隔控制,如果不控制,什么时候点击都会触发事件,一般一秒内允许按钮点击1到3次,这里就需要用Runtime实现,下面是我基于UIButton创建的一个分类:
.h
#import <UIKit/UIKit.h> @interface UIButton (XKButton) /** * 为按钮添加点击间隔 eventTimeInterval秒 */ @property (nonatomic, assign) NSTimeInterval eventTimeInterval; @end
.m
#import "UIButton+XKButton.h" #import <objc/runtime.h> #define xkDefaultClickInterval 0.5 //默认时间间隔 @interface UIButton () /** * bool YES 忽略点击事件 NO 允许点击事件 */ @property (nonatomic, assign) BOOL isIgnoreEvent; @end @implementation UIButton (XKButton) static const char * UIControl_eventTimeInterval = "UIControl_eventTimeInterval"; static const char * UIControl_enventIsIgnoreEvent = "UIControl_enventIsIgnoreEvent"; // runtime 动态绑定 属性 - (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{ objc_setAssociatedObject(self, UIControl_enventIsIgnoreEvent, @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isIgnoreEvent{ return [objc_getAssociatedObject(self, UIControl_enventIsIgnoreEvent) boolValue]; } - (NSTimeInterval)eventTimeInterval{ return [objc_getAssociatedObject(self, UIControl_eventTimeInterval) doubleValue]; } - (void)setEventTimeInterval:(NSTimeInterval)eventTimeInterval{ objc_setAssociatedObject(self, UIControl_eventTimeInterval, @(eventTimeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } + (void)load{ // Method Swizzling static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SEL selA = @selector(sendAction:to:forEvent:); SEL selB = @selector(_mqbd_sendAction:to:forEvent:); Method methodA = class_getInstanceMethod(self,selA); Method methodB = class_getInstanceMethod(self, selB); BOOL isAdd = class_addMethod(self, selA, method_getImplementation(methodB), method_getTypeEncoding(methodB)); if (isAdd) { class_replaceMethod(self, selB, method_getImplementation(methodA), method_getTypeEncoding(methodA)); }else{ //添加失败了 说明本类中有methodB的实现,此时只需要将methodA和methodB的IMP互换一下即可。 method_exchangeImplementations(methodA, methodB); } }); } - (void)_mqbd_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{ self.eventTimeInterval = self.eventTimeInterval == 0 ? xkDefaultClickInterval : self.eventTimeInterval; if (self.isIgnoreEvent){ return; }else if (self.eventTimeInterval > 0){ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.eventTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self setIsIgnoreEvent:NO]; }); } self.isIgnoreEvent = YES; [self _mqbd_sendAction:action to:target forEvent:event]; } @end
通过上述方法,可解决按钮重复点击,默认设置0.5秒内可点击一次;
但是在我调用相机 UIImagePickerController 的时候,问题出现了,点击拍照按钮,无反应,点击切换前后摄像头按钮,无反应,通过测试,发现是上面的文件内容导致。
问题处理
基于 UIImagePickerController 新建一个 XKBaseUIImagePickerController,调用相机时用 XKBaseUIImagePickerController
在 XKBaseUIImagePickerController 的 viewWillAppear 方法中,找到相机上的这几个按钮,然后对按钮添加 accessibilityIdentifier 标识:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; UIView *cameraView = [self findView:self.view withName:@"CAMCameraViewControllerContainerView"]; UIView *cropOverlay = [self findView:cameraView withName:@"CAMViewfinderView"]; UIView *bottomBar = [self findView:cropOverlay withName:@"CAMBottomBar"]; for (UIView *tmpView in bottomBar.subviews) { if ([NSStringFromClass([tmpView class]) isEqualToString:@"CUShutterButton"] || ///拍照按钮 [NSStringFromClass([tmpView class]) isEqualToString:@"CAMFlipButton"] || ///切换摄像头按钮 [NSStringFromClass([tmpView class]) isEqualToString:@"CAMReviewButton"]) { ///取消按钮 UIButton *shutButton = (UIButton *)tmpView; ///给按钮添加 accessibilityIdentifier shutButton.accessibilityIdentifier = mqb_filterIdentiferButtonIgnoreEvent; } } } -(UIView *)findView:(UIView *)aView withName:(NSString *)name{ Class cl = [aView class]; NSString *desc = [cl description]; if ([name isEqualToString:desc]) return aView; for (NSUInteger i = 0; i < [aView.subviews count]; i++) { UIView *subView = [aView.subviews objectAtIndex:i]; subView = [self findView:subView withName:name]; if (subView) return subView; } return nil; }
在给相机上各按钮添加标识后,在上面的 UIButton 分类文件 .m 文件中
修改以下方法,过滤掉添加标识的按钮即可
- (void)_mqbd_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{ self.eventTimeInterval = self.eventTimeInterval == 0 ? mqbDefaultClickInterval : self.eventTimeInterval; /// 过滤添加标识的按钮 if ([self.accessibilityIdentifier isEqualToString:mqb_filterIdentiferButtonIgnoreEvent]) { self.eventTimeInterval = 0.f; self.isIgnoreEvent = NO; } else{ if (self.isIgnoreEvent){ return; }else if (self.eventTimeInterval > 0){ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.eventTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self setIsIgnoreEvent:NO]; }); } self.isIgnoreEvent = YES; } [self _mqbd_sendAction:action to:target forEvent:event]; }