美文网首页iOS 进阶iOS新手学习ios-UI
IOS应用防止按钮连续点击

IOS应用防止按钮连续点击

作者: 东了个尼 | 来源:发表于2017-11-16 11:28 被阅读1711次
    THI Professional
    Is life always this hard,or is it just when you are a kid?
    Always like this.
    
    

    在软件开发项目中,我们经常会碰到点击按钮发送网络请求,或者点击按钮进行页面之间的逻辑跳转。但是有时候遇到一些卡顿的话,用户可能会失去耐心,然后疯狂的点击,这时候就会多次调用按钮触发的方法。在某些特定的情景下会导致页面重复push或者重复发送网络请求。这样的问题既对用户体验有影响,而且还会一定程度上增加服务器的压力。

    下面详细讲解几种解决思路,有不完善的地方 希望大家能够纠正。

    1.在按钮的触发方法内部做处理

    首先创建一个按钮button

    @interface ViewController ()
    @property(nonatomic,strong)UIButton *button;
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.button = [[UIButton alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
        self.button.backgroundColor = [UIColor redColor];    
        [self.button addTarget:self action:@selector(btnClickedOperations:) forControlEvents:UIControlEventTouchUpInside];
    
        [self.view addSubview:self.button];
    }
    
    - (void)btnClickedOperations:(id)sender{
                static NSTimeInterval time = 0.0;
                NSTimeInterval currentTime = [NSDate date].timeIntervalSince1970;
                //限制用户点击按钮的时间间隔大于1秒钟
                  
                    if (currentTime - time > 1) {
                    //处理逻辑
                        NSLog(@"这是一个测试");
                    }
                time = currentTime;
    }
    
    
    最终只有用户的点击时间间隔超过一秒钟 才会再次调用你要处理的逻辑代码。这样就实现了避免用户连续点击按钮带来的影响。 实现效果
    2.同样在方法内部做操作
    在按钮方法内部对按钮的状态进行控制 在执行完指定的操作后  用户才可以继续点击按钮  同样可以避免连续点击带来的问题   这种方法适合用在处理逻辑时间比较久的情况 ,如果处理逻辑时间很短暂 那么就起不到限制用户连续点击的情况
    - (void)test:(UIButton *)btn{
        btn.enabled = NO;
        //处理逻辑
        btn.enabled = YES;
    }
    
    如果想控制按钮的时间间隔同样可以加一个延迟的方法
    
    - (void)test:(UIButton *)btn{
        btn.enabled = NO;
        //处理逻辑
         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"btnClickedOperations");
            btn.enabled = YES;
        });
    }
    
    

    实现效果
    [图片上传中...(屏幕快照 2017-11-16 上午10.15.17.png-86dc45-1510798532321-0)]


    屏幕快照 2017-11-16 上午9.53.55.png
    3.重写button内部的 - (void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event;方法

    只适合新项目,不太适合老项目,用的地方太多了

    分析
    iOS中的按钮事件机制 >>> Target-Action机制
    用户点击时,产生一个按钮点击事件消息
    这个消息发送给注册的Target处理
    Target接收到消息,然后查找自己的SEL对应的具体实现IMP正儿八经的去处理点击事件
    实际上该点击消息包含三个东西:
    Target处理者
    SEL方法Id
    按钮事件当时触发时的状态
    
    具体实现
    @interface MyButton : UIButton
    /**
     按钮点击的间隔时间
     */
    @property(nonatomic,assign)NSTimeInterval time;
    @end
    

    .m文件

    #import "MyButton.h"
    
    
    static const NSTimeInterval defaultDuration = 3.0f;
    //记录按钮是否忽略按钮点击事件,默认第一次执行事件
    static BOOL _isIgnoreEvent = NO;
    
    /**
     设置执行按钮事件状态
     */
    static void resetState (){
        
        _isIgnoreEvent = NO;
        
    }
    @interface MyButton()
    
    @end
    
    @implementation MyButton
    - (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
        //1.按钮点击间隔事件
        _time = _time = (0) ? defaultDuration :_time;
        //2.是否忽略按钮点击事件
        if (_isIgnoreEvent) {
    //        2.1忽略按钮点击事件
    //        2.1忽略此事件
            return;
        }else if (_time >0){
            //不要忽略按钮的点击事件
            //后续在事件间隔内直接忽略掉按钮事件
            _isIgnoreEvent = YES;
            //间隔事件后  执行按钮事件
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                resetState();
            });
            //发送按钮点击消息
            [super sendAction:action to:target forEvent:event];
        }
    }
    @end
    

    实现效果

    屏幕快照 2017-11-16 上午10.15.17.png

    4使用UIButton Category封装防止按钮连续点击的具体实现

    分析:
    其实大体上逻辑和上面的实现差不多,只是因为在Category分类里面,无法完成重写sendAction:to:forEvent:对应的实现,只能通过运行时替换掉sendAction:to:forEvent:具体实现之后拦截到UIButton的sendAction:to:forEvent:方式执行时,将上面例子的逻辑加进来.
    

    具体实现代码

    #import <UIKit/UIKit.h>
    
    @interface UIButton (Tool)
    /**
    *  按钮点击的间隔时间
    */
    @property (nonatomic, assign) NSTimeInterval clickDurationTime;
    @end
    
    
    #import "UIButton+Tool.h"
     #import <objc/runtime.h>
    
     // 默认的按钮点击时间
     static const NSTimeInterval defaultDuration = 3.0f;
    
     // 记录是否忽略按钮点击事件,默认第一次执行事件
     static BOOL _isIgnoreEvent = NO;
    
    // 设置执行按钮事件状态
     static void resetState() {
             _isIgnoreEvent = NO;
     }
    @implementation UIButton (Tool)
    
     + (void)load {
             SEL originSEL = @selector(sendAction:to:forEvent:);
             SEL mySEL = @selector(my_sendAction:to:forEvent:);
        
             Method originM = class_getInstanceMethod([self class], originSEL);
             const char *typeEncodinds = method_getTypeEncoding(originM);
        
            Method newM = class_getInstanceMethod([self class], mySEL);
             IMP newIMP = method_getImplementation(newM);
        
             if (class_addMethod([self class], mySEL, newIMP, typeEncodinds)) {
                     class_replaceMethod([self class], originSEL, newIMP, typeEncodinds);
                 } else {
                         method_exchangeImplementations(originM, newM);
                     }
         }
    
    - (void)my_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
        
             // 保险起见,判断下Class类型
          if ([self isKindOfClass:[UIButton class]]) {
            
                     //1. 按钮点击间隔事件
                    self.clickDurationTime = self.clickDurationTime == 0 ? defaultDuration : self.clickDurationTime;
            
                //2. 是否忽略按钮点击事件
                     if (_isIgnoreEvent) {
                             //2.1 忽略按钮事件
                             return;
                         } else if(self.clickDurationTime > 0) {
                                 //2.2 不忽略按钮事件
                    
                                 // 后续在间隔时间内直接忽略按钮事件
                                 _isIgnoreEvent = YES;
                    
                                 // 间隔事件后,执行按钮事件
                                 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.clickDurationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                                         resetState();
                                     });
                    
                                 // 发送按钮点击消息
                                 [self my_sendAction:action to:target forEvent:event];
                             }
            
                 } else {
                         [self my_sendAction:action to:target forEvent:event];
                     }
         }
     #pragma mark - associate
    //由于分类不能增加属性 所以需要使用运行时动态绑定属性
     - (void)setClickDurationTime:(NSTimeInterval)clickDurationTime {
             objc_setAssociatedObject(self, @selector(clickDurationTime), @(clickDurationTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
         }
    
     - (NSTimeInterval)clickDurationTime {
             return [objc_getAssociatedObject(self, @selector(clickDurationTime)) doubleValue];
       }
    

    我们的按钮类不需要做任何的事情,完全不知道被拦截附加完成了防止连续点击的逻辑.

    使用

        UIButton *customeBut = [[UIButton alloc] initWithFrame:CGRectMake(300, 400, 100, 100)];
        
        customeBut.backgroundColor = [UIColor greenColor];
        //设置间隔为4秒。
        customeBut.clickDurationTime = 4.0f;
        [customeBut addTarget:self action:@selector(customeButOperations:) forControlEvents:UIControlEventTouchUpInside];
        
        [self.view addSubview:customeBut];
    
    - (void)customeButOperations:(UIButton *)sender{
        
       NSLog(@"%@", NSStringFromSelector(_cmd));
    }
    

    实现效果


    屏幕快照 2017-11-16 上午10.49.04.png

    以上就是处理按钮连续点击问题的解决办法,希望能对你有所帮助,文章中的代码已经上传到github.

    相关文章

      网友评论

      • 萧城x:双击会有影响吗
        东了个尼:@Jason_cjc 应该不会的,已经做了限制,所以双击不会连续调用方法
      • rxdxxxx:1. 因为_isIgnoreEvent这个是静态的, 会影响到其他按钮
        2. 如果其他UIControl也添加了target action 会报找不到方法.
        东了个尼:@RedRain 嗯嗯 不错的建议,不过如果需要的是所有的按钮都限制点击次数,我这样也可以,这里只提供实现的思路,具体需要看实际需求去做:blush:
        rxdxxxx:@东了个尼
        1. 给UIControl 增加了 属性来控制是否在执行ignore, 就不会影响其他的控件了
        2.这个分类方法给UIControl, 不给UIButton就好了
        东了个尼:我重写了父类的方法,拦截了父类的方法,如果其他UIControl也添加了target action 不会报找不到方法,只是会同样也会使按钮不能连续点击,同样设置了时间间隔,你可以自己测试下。:blush:

      本文标题:IOS应用防止按钮连续点击

      本文链接:https://www.haomeiwen.com/subject/pxzcvxtx.html