iOS 按钮、Cell暴力点击触发多次响应的问题研究

来源:互联网 发布:互联网十云计算概念股 编辑:程序博客网 时间:2024/06/05 10:13

在app使用的过程中,由于网络差,手机卡顿等问题,用户在不经意的时候可能多次操作,造成多次请求、或者页面多次push等,给用户带来了不好的体验,那么如何解决相关的问题呢?本文介绍几种解决办法,在某种程度上可以减少这类事件的发生,但是都未能优雅完美的解决根本问题,如果读者有更好的解决办法,欢迎留言讨论。


一、按钮的暴力点击

方案一:通过设置enable属性

-(void)btnAction:(UIButton*)btn{    btn.enabled = NO;    // do something    btn.enabled = YES;}

点评:这种方案通过点击按钮时,将按钮的响应点击能力关闭,当完成某中业务(如数据获取)时再开启。

优点:满足我们最终想要的需求
缺点:每个按钮都需要这样手动设置,开启时需要格外的注意适当的时机,否则适得其反


方案二:设置响应点击事件的时间间隔(不推荐)

-(void)btnAction:(UIButton*)btn{    [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(doSomeThing) object:btn];    [self performSelector:@selector(doSomeThing) withObject:btn afterDelay:2.0f];// 时间间隔}-(void)doSomeThing{    NSLog(@"click");}

点评:这种方案是每次点击时,取消上次的响应事件,并设置为延迟执行,如果用户在延迟执行的时间间隔内一直点击,则点击事件一直被取消,不会执行,虽然可以避免用户的暴力操作,但是第一次点击都不会被执行,不能及时响应,用户体验极差,不推荐,除非业务需要就是如此

优点:无


方案三:分类

该例子实现了UIButton的父类UIControl,通过运行时来实现按钮的间断响应功能

.h 文件

@interface UIControl (NonViolentClicked)// 接受事件响应的时间间隔,开放出来供外部设置@property (nonatomic, assign) NSTimeInterval acceptEventInterval;@end

.m 文件

#import <objc/runtime.h> // 运行时@interface UIControl ()@property (nonatomic, assign) BOOL ignoreEvent; // 是否忽视点击的事件@end@implementation UIControl (NonViolentClicked)// 使用运行时关联UIControl的点击事件+(void)load{    Method a = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));    Method b = class_getInstanceMethod(self, @selector(custom_sendAction:to:forEvent:));    method_exchangeImplementations(a, b);}// 自定义的点击事件- (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{    if (self.ignoreEvent) return;    if (self.acceptEventInterval > 0){        self.ignoreEvent = YES;        [self performSelector:@selector(setIgnoreEvent:) withObject:@(NO) afterDelay:self.acceptEventInterval];    }    [self custom_sendAction:action to:target forEvent:event];}// 实现acceptEventInterval的getter方法-(NSTimeInterval)acceptEventInterval{    return [objc_getAssociatedObject(self, _cmd) doubleValue]?[objc_getAssociatedObject(self, _cmd) doubleValue]:1.0; // 默认的响应时间为1秒}// 实现acceptEventInterval的setter方法-(void)setAcceptEventInterval:(NSTimeInterval)acceptEventInterval{    objc_setAssociatedObject(self, @selector(acceptEventInterval), [NSNumber numberWithDouble:acceptEventInterval], OBJC_ASSOCIATION_ASSIGN);}// 实现ignoreEvent的getter方法-(BOOL)ignoreEvent{    return [objc_getAssociatedObject(self, _cmd) doubleValue];}// 实现ignoreEvent的setter方法-(void)setIgnoreEvent:(BOOL)ignoreEvent{    objc_setAssociatedObject(self, @selector(ignoreEvent), [NSNumber numberWithBool:ignoreEvent], OBJC_ASSOCIATION_ASSIGN);}

使用:

-(void)btnAction:(UIButton*)btn{    NSLog(@"click");}

点评:上述两种的结合,使用分类并设置点击响应时间间隔

优点:可以在不改变原有UIButton点击事件的基础上减少暴力点击的发生,一劳永逸;相比方案二,可以及时响应点击事件,并在时间间隔后继续响应

缺点:需要新建分类文件,代码多;通过设置响应时间间隔并不能完美解决暴力点击的问题,因为有时候网络请求慢,用户可能会在时间间隔外再次点击按钮(这时候则需要我们取消上次网络请求)


方案四:分类的另外一种设想

我的另外一种想法是,在点击事件发生时,即改变按钮的enable属性为NO,锁定按钮不可点击,在某个阶段时再将enable设为YES

则分类文件中,自定义响应事件变为:

- (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{    self.enabled = NO; // 不响应点击事件    [self custom_sendAction:action to:target forEvent:event];}

再执行完点击业务时再将enable设置为YES;

点评:这种方案结合了方案一和方案三,但是个人认为这样做还不如方案一来的简洁直观

优点:只需在合适的时机开启enable;可以手动设置enable,掌握可响应时机
缺点:不直观

总结:个人还是推荐方案一和方案三,当然方案一想必是我们一直使用的手段,方案三提供了一种新的解决思路。如果方案四UIControl的分类可以自身获取到开启enable的时机,不失为暴力点击最完美的解决方案了,可以笔者能力有限,如果有读者有更好的解决方案,欢迎赐教。





二、cell的暴力点击

本着模仿UIButton的思路来解决cell的暴力点击,但是UITableView和UIBttoun的种种的不同,都未能找到较好的方案,只能粗暴一点的处理了。。。

暴力方案:使用标志符

{   // 新增cell点击标记    BOOL cellSelected;}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    if (cellSelected) {        return;    }else{        cellSelected = YES;        [self performSelector:@selector(didSelectRowAtIndexPath:) withObject:indexPath];        //[self performSelector:@selector(didSelectRowAtIndexPath:) withObject:indexPath afterDelay:2.0]; // 延迟执行,不推荐    }}-(void)didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    NSLog(@"%ld",indexPath.row);    // 模拟事件处理,2秒之后再次响应    // do something    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        cellSelected = NO;    });}

点评:这种方法适用大部分的响应事件,包括按钮点击事件,tableView的点击事件,或者手势等,只是这样处理显得太粗暴,一点都不优雅,但是笔者现在能力有限,实在想不到其他比较好的方式了,555,如果读者有更好的解决方案,不吝赐教啊,感谢!


三、相关阅读

1、iOS 分类和扩展

2、iOS 运行时(runtime)

3、iOS UIButton点击事件传递参数的解决办法

阅读全文
0 0
原创粉丝点击