iOS7 UIBarButtonItem 无法响应点击事件

最近做项目时就碰到了UIBarButttonItem无法响应点击事件的问题。

原因

找了原因才发现是因为在viewController.view上添加了UITapGestureRecognizer。然后那个Tap Recognizer捕获了所有的tap事件, 导至点击toolbar上的ButtonItem没响应。
看官可能会说,按respond chain, toolbar上的事件不应该会被tap recognizer捕获才对, toolbar是viewController.view的subView。可是不辛的是,ios7上就是这么不讲道理。

解决方案

  1. 移除Tap Recognizer
    如果可能的话,移除Tap Recognizer,toolbar就可以响应事件了。

  2. 利用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上,则进行相关处理,并返回。否则,还是原来的处理逻辑。

你可能感兴趣的:(iOS7 UIBarButtonItem 无法响应点击事件)