问题
开发中遇到了个问题:点击tableview的cell,没有调用didselect方法
分析原因
- 当然先排除一些低级错误:是否忘了为tableview设置了代理;是否有其他view遮挡了cell;是否设置了tableview或者cell的userInteractionEnabled为NO等
排除这些之后发现依然没有调用didSelect方法 - 然后去查原因,后来终于发现是事件响应优先级出了问题:
当前View称为AView吧,我在AView上添加了tableview,点击cell不调用didSelect方法,后来发现是因为我在AView的父view上添加了手势用于收起键盘:
- (void)addTabGesture {
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
tap.delegate = self;
[self addGestureRecognizer:tap];
// 监听键盘收起
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHideFunc:) name:UIKeyboardWillHideNotification object:nil];
}
单击事件优先传递给手势响应,如果手势响应识别成功,就会直接取消事件的响应链传递。
这样当我点击cell的时候,点击事件首先会被手势识别器捕获到,就无法再继续执行tableview的代理方法了
解决
》是否可以捕获到点击动作,在这个时候判断点击的是哪个view,如果是tableview的cell,就不继续执行tapGesture,如果不是再继续。
》UIGestureRecognizerDelegate中有一个代理方法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
,
官方解释是:
called before touchesBegan:withEvent: is called on the gesture recognizer for a new touch. return NO to prevent the gesture recognizer from seeing this touch
:
在touchesBegan方法之前执行,返回NO就可以防止手势识别器识别到这个touch。
那么这个方法就可以满足我们的需求了
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// 获取当前点击所在的view
NSString *classStr = NSStringFromClass([touch.view class]);
// 判断是否是cell
if ([classStr isEqualToString:@"UITableViewCellContentView"]) {
return NO;
}
return YES;
}
查看tableveiwcell的层级可以发现,我们点击cell获取到点击事件的其实是contentView
所以代码中要判断所点击view的类是否是UITableViewCellContentView,自定义cell也是如此
其他
在网上查找的过程中,发现了别人由于其他原因导致同样问题的,也写在这里记录一下:
- 使用xib自定义tableview,用连线方式设置代理,但是不小心线连错了😓
- 使用了xib自定义tableView,在xib中设置了tableView的selection为no selection的。或者在代码中设置了tableView的allowsSelection为NO的
- 实现了
willSelectRowAtIndexPath
方法:
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
- 实现了touchesBegin方法(这个其实跟我的问题是一个原因)
- 子view的范围超出父view的范围
这个时候子view虽然可以正常显示出来,但范围在父view范围之外的部分,是完全不能响应点击事件的