您的位置:首页 > 移动开发 > IOS开发

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

2017-07-31 15:18 423 查看
在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点击事件传递参数的解决办法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios