最近做項目時就碰到了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上,則進行相關處理,并返回。否則,還是原來的處理邏輯。