Objective-C:Runtime+ButtonClicked
Page content
一个优秀的解决方案,不仅高效,而且优雅
引言
在项目开发过程中,肯定遇到过按钮关联的方法有网络请求或者其他相类似的逻辑操作,这时会产生这样一种需求:避免按钮频繁点击造成的频繁逻辑处理(如频繁访问接口),以免造成服务器压力或其他不可预知的状况。
当然,解决问题的方式多种多样,举个最简单的例子:
~~~objective-c
- (IBAction)acceptBackOrder:(UIButton *)sender {
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(sellerVerifyBackOrderWithKind:) object:@(30)];
[self performSelector:@selector(sellerVerifyBackOrderWithKind:) withObject:@(30) afterDelay:0.35];
}
~~~
通过在时段内取消上一次任务,可已解决这个问题,但是每一个需要这种操作的button方法都要这样写,造成冗余,却不够优雅。 那么,怎样优雅的解决这个问题呢?这就是这篇文章的正题:使用Runtime优雅的给Button添加连续点击限定。
实例
首先,给UIButton添加Category:UIButton+DelayEvent 接口文件如下:
~~~objective-c
@interface UIButton (DelayEvent)
@property (nonatomic, assign) NSTimeInterval delayInterval;//添加点击事件的间隔时间
@property (nonatomic, assign) BOOL delayIgnoreEvent;
@end
~~~
实现文件如下:
~~~objective-c
@implementation UIButton (DelayEvent)
/*
* Runtime 给Category添加属性
*/
static const char *UIControl_acceptEventInterval = "UIControl_acceptEventInterval";
static const char *UIcontrol_ignoreEvent = "UIcontrol_ignoreEvent";
- (NSTimeInterval)delayInterval {
return [objc_getAssociatedObject(self, UIControl_acceptEventInterval) doubleValue];
}
- (void)setDelayInterval:(NSTimeInterval)delayInterval {
objc_setAssociatedObject(self, UIControl_acceptEventInterval, @(delayInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)delayIgnoreEvent {
return [objc_getAssociatedObject(self, UIcontrol_ignoreEvent) boolValue];
}
- (void)setDelayIgnoreEvent:(BOOL)delayIgnoreEvent {
objc_setAssociatedObject(self, UIcontrol_ignoreEvent, @(delayIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
/*
* load 方法进行 Method Swizzling
*/
+ (void)load {
// Tabbar以及ToolBar等特殊情况,添加一步判断,避免这类按钮点击闪退(把此功能局限于Button)
if ([self respondsToSelector:@selector(delayIgnoreEvent)]) {
Method a = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method b = class_getInstanceMethod(self, @selector(__delay_sendAction:to:forEvent:));
method_exchangeImplementations(a, b);
}
}
/*
* 交换后的方法,便于我们控制
*/
- (void)__delay_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
if (self.delayIgnoreEvent) return;
if (self.delayInterval > 0) {
self.delayIgnoreEvent = YES;
[self performSelector:@selector(setDelayIgnoreEvent:) withObject:@(NO) afterDelay:self.delayInterval];
}
[self __delay_sendAction:action to:target forEvent:event];
}
~~~
附上参考的该解决方案的地址:http://www.cocoachina.com/ios/20150911/13260.html