iOS 进阶iOS新手学习ios-UI

IOS应用防止按钮连续点击

2017-11-16  本文已影响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.

上一篇下一篇

猜你喜欢

热点阅读