-->

hitTest fires when UIKeyboard is tapped

2019-02-13 13:17发布

问题:

I'm trying to fix a bug that involves UIView hitTest:withEvent: being called on my view when the touches are on the UIKeyboard, but only after the app has been in the background.

It was occurring in my app with a complex view hierarchy so I reproduced it in an app with only 2 views:

  • 1 UIView 768x1024 (fullscreen)
  • 1 UITextView 200x200 in the upper half of the fullscreen view

The behavior is as follows:

  • Tapping the textview causes the fullscreen view's hitTest method to fire, the textfield becomes first responder, and then the keyboard appears all as expected. Tapping on keyboard keys works fine.
  • Now dismiss the keyboard.
  • Send the app to the background.
  • Then resume the app.
  • Make the textview first responder again. Here's the trouble, now when tapping keys on the keyboard the fullscreen view's hitTest method is firing.

I'm seeing this on an iOS 5 iPad 2. Only on device though, never in the simulator. Any idea why hitTesting could get messed up in this way? Thanks.

回答1:

Got the same issue here. It does happen ONLY when I hit home and return to the app. Does not happen in the first fresh run.

And it is related to iOS5 as well.



回答2:

The issue described above seems to be caused by the keyboard's UIWindow getting stuck in a bad state. Ensuring that the keyboard window's hidden property gets set to YES (even if it is already YES) fixes the problem for me. This can be done in your UIApplicationDelegate class:

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // The keyboard sometimes disables interaction when the app enters the 
    // background due to an iOS bug. This brings it back to normal.
    for (UIWindow *testWindow in [UIApplication sharedApplication].windows) {
        if (!testWindow.opaque && [NSStringFromClass(testWindow.class) hasPrefix:@"UIText"]) {
            BOOL wasHidden = testWindow.hidden;
            testWindow.hidden = YES;

            if (!wasHidden) {
                testWindow.hidden = NO;
            }

            break;
        }
    }
}

The class name of the keyboard window, at least in iOS 5 with a standard US keyboard, is UITextEffectsWindow. As usual it is not a good idea to rely on undocumented class names but in the case of an OS-specific bug it works for my purposes. There could be any number of windows, including the root application window, keyboard, alerts, and other windows that your app or other frameworks have added, so don't be too inspecific.



回答3:

I got the same problem, and my work around is to listen to UIKeyboardDidShowNotification and UIKeyboardDidHideNotification, calculate the keyboard height using UIKeyboardFrameEndUserInfoKey, then in my hitTest:withEvent: method I would see whether the hit was on the keyboard "zone" or not.



回答4:

Just to expand on the answer by @enzo-tran , this is what I ended up doing: I added a keyboardRect property to my UIView subclass, registered for UIKeyboardDidShowNotification and UIKeyboardDidHideNotification, and added:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  if (CGRectContainsPoint([self keyboardRect], point)) {
    // Ignore      
  } else {
    ...
  }
}

- (void)keyboardDidShow:(NSNotification *)notif {
    CGRect keyboardRect;

    [[[notif userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
    keyboardRect = [self convertRect:keyboardRect fromView:nil];

    [self setKeyboardRect:keyboardRect];
}

- (void)keyboardDidHide:(NSNotification *)notif {
    [self setKeyboardRect:CGRectZero];
}