刚开始,开发一个新功能,需要用到左滑编辑,删除功能。代码如下:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
//第二组可以左滑删除
if (indexPath.section == 0) {
return YES;
}
return NO;
}
/**
* 只要实现了这个方法,左滑出现按钮的功能就有了
* (一旦左滑出现了N个按钮,tableView就进入了编辑模式, tableView.editing = YES)
*/
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
}
/**
* 左滑cell时出现什么按钮
*/
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
__weak typeof(self) weakSelf = self;
UITableViewRowAction *actionEdit = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"编辑" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
NSLog(@"点击了编辑");
// 收回左滑出现的按钮(退出编辑模式)
tableView.editing = NO;
[weakSelf editSubscribeInfoAction:indexPath];
}];
NSInteger row = indexPath.row;
NSString *title = @"显示";
if (self.dataArray.count > row) {
LFGetSubScribeByListOutputModel *model = self.dataArray[row];
if ([model.state isEqualToString:@"0"]) {
title = @"隐藏";
}
}
UITableViewRowAction *actionHide = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
NSLog(@"点击了隐藏");
// 收回左滑出现的按钮(退出编辑模式)
tableView.editing = NO;
if ([title isEqualToString:@"显示"]) {
// 显示
[weakSelf editSubscribeAction:indexPath];
}else {
// 隐藏
[weakSelf showHideSubcribeMessageViewAt:indexPath];
}
}];
UITableViewRowAction *actionDel = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
NSLog(@"点击了删除");
// 收回左滑出现的按钮(退出编辑模式)
tableView.editing = NO;
weakSelf.currentEditIndexPath = indexPath;
[weakSelf showUnauthenticatedView];
}];
return @[actionDel, actionHide, actionEdit];
}
功能基本实现,点击效果也还行.
后来由于人的本性--太懒,在实现“屏蔽用户频繁触发点击事件”的功能时,为了所谓的“一劳永逸”“简单”,便使用了button的分类( UIButton+FixMultiClick )来实现该功能,思路是在分类中使用自己写的新的按钮响应方法替换系统的按钮响应方法,并在自己的响应方法中添加拦截,即:如果两次连续点击时间间隔小于设定时间,则不响应点击方法,如果大于则只需响应方法.
核心代码如下:
// 因category不能添加属性,只能通过关联对象的方式。
static const char *UIControl_acceptEventInterval = "UIControl_acceptEventInterval";
// getter方法
- (NSTimeInterval)mjk_acceptEventInterval {
return [objc_getAssociatedObject(self, UIControl_acceptEventInterval) doubleValue];
}
// setter方法
- (void)setMjk_acceptEventInterval:(NSTimeInterval)mjk_acceptEventInterval {
objc_setAssociatedObject(self, UIControl_acceptEventInterval, @(mjk_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
static const char *UIControl_acceptEventTime = "UIControl_acceptEventTime";
// getter方法
- (NSTimeInterval)mjk_acceptEventTime {
return [objc_getAssociatedObject(self, UIControl_acceptEventTime) doubleValue];
// return [objc_getAssociatedObject(self, _cmd) doubleValue];
}
// setter方法
- (void)setMjk_acceptEventTime:(NSTimeInterval)mjk_acceptEventTime {
objc_setAssociatedObject(self, UIControl_acceptEventTime, @(mjk_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// objc_setAssociatedObject(self, @selector(mjk_acceptEventTime), @(mjk_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// 在load时执行hook
/**
* 注释:
* load方法是在objc库中的一个load_images函数中调用的.
* 先把二进制映像文件中的头信息取出, 再解析和读出各个模块中的类定义信息, 把实现了load方法的类和Category记录下来, 最后统一执行调用.
* 主类中的load方法的调用时机要早于Category中的load方法.
*/
+ (void)load {
[super load];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// 分别获取
SEL beforeSelector = @selector(sendAction:to:forEvent:);
SEL afterSelector = @selector(mjk_sendAction:to:forEvent:);
Method beforeMethod = class_getInstanceMethod(class, beforeSelector);
Method afterMethod = class_getInstanceMethod(class, afterSelector);
/**
* 先尝试给原来的方法添加实现,如果原来的方法不存在就可以添加成功。
* 返回为YES,否则返回为NO。
*/
// UIButton 真的没有sendAction方法的实现,这是继承了UIControl的而已,UIControl才真正的实现了。
BOOL didAddMethod =
class_addMethod(class,
beforeSelector,
method_getImplementation(afterMethod),
method_getTypeEncoding(afterMethod));
NSLog(@"%d",didAddMethod);
if (didAddMethod) {
// 如果之前不存在,但是添加成功了,此时添加成功的是cs_sendAction方法的实现
// 这里只需要方法替换
class_replaceMethod(class,
afterSelector,
method_getImplementation(beforeMethod),
method_getTypeEncoding(beforeMethod));
} else {
//本来如果存在就进行交换
method_exchangeImplementations(afterMethod, beforeMethod);
}
});
// Method before = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
// Method after = class_getInstanceMethod(self, @selector(mjk_sendAction:to:forEvent:));
// method_exchangeImplementations(before, after);
}
- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
[super sendAction:action to:target forEvent:event];
}
- (void)mjk_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
NSTimeInterval time = self.mjk_acceptEventInterval;
if (time <= 0.0) {
time = DEFAULT_ACCEPT_EVENT_INTERVAL;
self.mjk_acceptEventInterval = time;
}
if ([NSDate date].timeIntervalSince1970 - self.mjk_acceptEventTime < time) {
return;
}
// 第一次点击的时间
self.mjk_acceptEventTime = [NSDate date].timeIntervalSince1970;
[self mjk_sendAction:action to:target forEvent:event];
}
到此,项目中几乎所有的按钮无需添加任何代码便实现了防止重复点击事件.
但问题也自此出现,就是 ,cell左滑出现的按钮点击响应出现了问题, 无法正常响应.
另一个问题是,代码跟踪后发现, 不同cell上的 左滑出现的按钮的 acceptEventTime似乎是通用的, 即,如果点击了某个cell上的某个左滑按钮,则其他cell上相应位置的按钮在 设定的限制时间内也不能响应点击事件.
解决方法: 代码跟踪发现, 左滑点击按钮的响应方法 都为“_pressedButton:”, 因此拙劣的、粗暴的解决方是 在自定义的 用来替换按钮响应方法中 进行判断如果是 方法是“_pressedButton:”则直接执行响应方法,不再做重复点击判断.
代码如下:
- (void)mjk_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
/**
* SEL 变量名 = NSSelectorFromString(方法名字的字符串);
* NSString *method = NSStringFromSelector(setWidthHeight)
*/
NSString *method = NSStringFromSelector(action);
if ([method isEqualToString:@"_pressedButton:"]) {
NSLog(@"******************>>> _pressedButton: <<<************");
[self mjk_sendAction:action to:target forEvent:event];
return;
}
NSTimeInterval time = self.mjk_acceptEventInterval;
if (time <= 0.0) {
time = DEFAULT_ACCEPT_EVENT_INTERVAL;
self.mjk_acceptEventInterval = time;
}
if ([NSDate date].timeIntervalSince1970 - self.mjk_acceptEventTime < time) {
return;
}
// 第一次点击的时间
self.mjk_acceptEventTime = [NSDate date].timeIntervalSince1970;
[self mjk_sendAction:action to:target forEvent:event];
}
马马虎虎,凑合着先用.如有更好方法再进行修改.
防止按钮重复点击代码,下载地址:https://download.youkuaiyun.com/download/pjc6362645/10922812