ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

iOS-用Runtime解决Button重复点击引发的相机按钮问题

2021-01-09 17:34:01  阅读:213  来源: 互联网

标签:eventTimeInterval Button iOS isIgnoreEvent 点击 UIButton 按钮 Runtime self


引入

在项目中经常能用到一个功能,就是对于按钮的点击时间间隔控制,如果不控制,什么时候点击都会触发事件,一般一秒内允许按钮点击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];
}

标签:eventTimeInterval,Button,iOS,isIgnoreEvent,点击,UIButton,按钮,Runtime,self
来源: https://www.cnblogs.com/wangkejia/p/14255570.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有