最近做项目时就碰到了UIBarButttonItem无法响应点击事件的问题。
原因
找了原因才发现是因为在viewController.view上添加了UITapGestureRecognizer。然后那个Tap Recognizer捕获了所有的tap事件, 导至点击toolbar上的ButtonItem没响应。
看官可能会说,按respond chain, toolbar上的事件不应该会被tap recognizer捕获才对, toolbar是viewController.view的subView。可是不辛的是,ios7上就是这么不讲道理。
解决方案
-
移除Tap Recognizer
如果可能的话,移除Tap Recognizer,toolbar就可以响应事件了。 -
利用UIGestureRecognizerDelegate来阻止Tap Recognizer捕获事件
UIGestureRecognizerDelegate 有这么一个函数,用来决定gesture Recognizer要不要响应touch。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
我是这样做的:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view.superview == self.datePicker.toolBar) {
return NO;
}
return YES;
}
上面代码,如果touch发生在toolbar中,我们就让gesture recognizer忽略此touch。这样toolbar应该就能正常响应事件了。可是iOS7又再次不讲道理, 即便我返回了NO。tap recognizerd还是不要脸的捕获了事件!
好吧,算你狠。既然改变不了你,只好改变自己。我的方案是在tap recognizer的响应函数分析、并发送处理toolbar事件。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view.superview == self.datePicker.toolBar) {
self.touch = touch;
// only work for non ARC
// NSString *title;
// object_getInstanceVariable(touch, "_title", (void *)&title);
// for ARC
NSLog(@"%@",touch.view);
Ivar titleIvar = class_getInstanceVariable([touch.view class], "_title");
NSString *title = object_getIvar(touch.view, titleIvar);
if ([title isEqualToString:@"完成"]) {
self.datePickerDone = YES;
} else if ([title isEqualToString:@"取消"]) {
self.datePickerCanceled = YES;
}
return NO;
}
self.touch = nil;
return YES;
}
- (void)dismissKeyboard:(UIGestureRecognizer *)gesture {
if (self.touch) {
if (self.datePickerCanceled) {
[self.datePicker cancelBtnAction:self];
self.datePickerCanceled = NO;
}
if (self.datePickerDone) {
[self.datePicker doneBtnAction:self];
self.datePickerDone = NO;
}
return;
}
[self.view endEditing:false];
}
在函数- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
中分析touch,如果发生在toolbar,则记录是哪个barButtonItem发生了点击。UIBarButtonItem只是存数据的model,真正创建toolbar时会根据UIBarButtonItem来创建view(UIToolbarButton),该view是私有类,我们不知道它的接口,打断时可以发现它有个_title变量,就是我们设置barButtonItem时设的title。我们可以根据这个变量来区分哪个barButtonItem被点击了。而如何在ARC下,获取类的私有变量:
Ivar titleIvar = class_getInstanceVariable([touch.view class], "_title");
NSString *title = object_getIvar(touch.view, titleIvar);
在记录了点击位置之后,我们就可以在tap的响应函数中进行处理,我这里是- (void)dismissKeyboard:(UIGestureRecognizer *)gesture
。如果前面的delegate记录点击发生在toolbar上,则进行相关处理,并返回。否则,还是原来的处理逻辑。
网友评论