今天发现项目中一个bug,猛点按钮进入下一控制器时,结果push了两次,然后点击返回,黑屏了,主要原因在于,猛点的时候,处理点击事件时又重新响应了相应事件,也就是说响应方法在猛点下调用了两次。
解决办法,就是让按钮点击处理事件的一秒内(不一定一秒可以自己测试一下)不能重复响应事件,于是写了个UIButton分类。
代码如下:
头文件:
#import
@interface UIButton (Extent)
@property(nonatomic,assign)BOOL isDoing;
@property(nonatomic,copy)NSString *actionSelName;
@property(nonatomic,strong)id target;
@end
源文件:
static const char bntExtentIsDoing;
static const char BtnExtentActionSel;
static const char BtnExtentTarget;
#import "UIButton+Extent.h"
#import
@implementation UIButton(Extent)
-(void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
{
NSString *actionSelName = NSStringFromSelector(action);
[self setActionSelName:actionSelName];
[self setTarget:target];
[self setIsDoing:NO];
//替换按钮点击的响应事件
return [super addTarget:self action:@selector(btnExtentClicked:) forControlEvents:controlEvents];
}
-(void)btnExtentClicked:(UIButton*)btn
{
BOOL isDoing = [self isDoing];
[self setIsDoing:YES];
if (!isDoing) {
SEL action = NSSelectorFromString([self actionSelName]);
id target = [self target];
/*
ARC情况下会有警告,因为这种调用,action是通过字符串创建的,运行时才能决定 action对应方法的函数签名,比如有返回值,编译器不知道该如何处理,存在出现内存泄漏情况, 而编译器不知道,故而给出警告。那就直接使用IMP指针避免让人看了烦躁的警告!
[target performSelector:action withObject:btn];
*/
IMP imp = [target methodForSelector:action];
void (*funaction)(id target,SEL sel,id btn) = (void*)imp;
funaction(target,action,btn);
[self performSelector:@selector(resetIsDoing) withObject:nil afterDelay:1];
}
}
-(void)resetIsDoing
{
[self setIsDoing:NO];
}
-(id)target
{
return objc_getAssociatedObject(self, &BtnExtentTarget);
}
-(void)setTarget:(id)target
{
objc_setAssociatedObject(self, &BtnExtentTarget, target, OBJC_ASSOCIATION_ASSIGN);
}
-(NSString*)actionSelName
{
return objc_getAssociatedObject(self, &BtnExtentActionSel);
}
-(void)setActionSelName:(NSString *)actionSelName
{
objc_setAssociatedObject(self, &BtnExtentActionSel, actionSelName, OBJC_ASSOCIATION_COPY);
}
-(void)dealloc
{
objc_removeAssociatedObjects(self);
}
-(BOOL)isDoing
{
id result = objc_getAssociatedObject(self, &bntExtentIsDoing);
return [result boolValue];
}
-(void)setIsDoing:(BOOL)isDoing
{
objc_setAssociatedObject(self, &bntExtentIsDoing, @(isDoing), OBJC_ASSOCIATION_RETAIN);
}
@end
在分类中主要添加了target,actionSelName,isDoing三个属性,target记录响应事件的的处理对象,也就是说当按钮点击后,向那个对象发送按钮点击消息,actionSelName是选择器名称,表示target使用actionSelName代表的选择器对应的方法处理点击事件,isDoing记录是否在处理点击事件,如果在处理点击事件,那么不在处理用户重复点击的事件。