iOS: How to access the `UIKeyboard`?

12,820

Solution 1

How about using -[UIApplication beginIgnoringInteractionEvents]?

Also, another trick to get the view containing the keyboard is to initialize a dummy view with CGRectZero and set it as the inputAccessoryView of your UITextField or UITextView. Then, get its superview. Still, such shenanigans is private/undocumented, but I've heard of apps doing that and getting accepted anyhow. I mean, how else would Instagram be able to make their comment keyboard interactive (dismiss on swipe) like the Messages keyboard?

Solution 2

Try this:

// my func
- (void) findKeyboard {

    // Locate non-UIWindow.
    UIWindow *keyboardWindow = nil;
    for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
        if (![[testWindow class] isEqual:[UIWindow class]]) {
           keyboardWindow = testWindow;
           break;
       }
    }

    // Locate UIKeyboard.  
    UIView *foundKeyboard = nil;
    for (UIView *possibleKeyboard in [keyboardWindow subviews]) {

        // iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
        if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"]) {
            possibleKeyboard = [[possibleKeyboard subviews] objectAtIndex:0];
        }                                                                                

        if ([[possibleKeyboard description] hasPrefix:@"<UIKeyboard"]) {
           foundKeyboard = possibleKeyboard;
           break;
        }
    }
}   

Solution 3

I found that developerdoug's answer wasn't working on iOS 7, but by modifying things slightly I managed to get access to what I needed. Here's the code I used:

-(UIView*)findKeyboard
{
    UIView *keyboard = nil;

    for (UIWindow* window in [UIApplication sharedApplication].windows)
    {
        for (UIView *possibleKeyboard in window.subviews)
        {
            if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"])
            {
                keyboard = possibleKeyboard;
                break;
            }
        }
    }

    return keyboard;
}

From what I could make out, in iOS 7 the keyboard is composed of a UIPeripheralHostView containing two subviews: a UIKBInputBackdropView (which provides the blur effect on whatever's underneath the keyboard) and a UIKeyboardAutomatic (which provides the character keys). Manipulating the UIPeripheralHostView seems to be equivalent to manipulating the entire keyboard.

Discaimer: I have no idea whether Apple will accept an app that uses this technique, nor whether it will still work in future SDKs.

Solution 4

Under iOS 8 it appears you have to jump down the chain more than in the past. The following works for me to get the keyboard, although with custom keyboards available and such I wouldn't rely on this working unless you're running in a controlled environment.

- (UIView *)findKeyboard {
    for (UIWindow* window in [UIApplication sharedApplication].windows) {
        UIView *inputSetContainer = [self viewWithPrefix:@"<UIInputSetContainerView" inView:window];
        if (inputSetContainer) {
            UIView *inputSetHost = [self viewWithPrefix:@"<UIInputSetHostView" inView:inputSetContainer];
            if (inputSetHost) {
                UIView *kbinputbackdrop = [self viewWithPrefix:@"<_UIKBCompatInput" inView:inputSetHost];
                if (kbinputbackdrop) {
                    UIView *theKeyboard = [self viewWithPrefix:@"<UIKeyboard" inView:kbinputbackdrop];
                    return theKeyboard;
                }
            }
        }
    }

    return nil;
}


- (UIView *)viewWithPrefix:(NSString *)prefix inView:(UIView *)view {
    for (UIView *subview in view.subviews) {
        if ([[subview description] hasPrefix:prefix]) {
            return subview;
        }
    }

    return nil;
}

Solution 5

For an app I am currently developing I am using a really quick and easy method:

Add this in the header file:

// Add in interface
UIWindow * _window;

// Add as property
@property (strong, nonatomic) IBOutlet UIView * _keyboard;

Then add this code in the bottom of the keyboardWillShow function:

-(void) keyboardWillShow: (NSNotification *) notification {

    .... // other keyboard will show code //

    _window = [UIApplication sharedApplication].windows.lastObject;

    [NSTimer scheduledTimerWithTimeInterval:0.05
                                     target:self
                                   selector:@selector(allocateKeyboard)
                                   userInfo:nil
                                    repeats:NO];
}

This code look for when the keyboard is raised and then allocates the current window. I have then added a timer to allocate the keyboard as there were some issues when allocated immediately.

- (void)allocateKeyboard {

    if (!_keyboard) {
        if (_window.subviews.count) {

            // The keyboard is always the 0th subview
            _keyboard = _window.subviews[0];
        }        
    }
}

We now have the keyboard allocated which gives you direct "access" to the keyboard as the question asks.

Hope this helps

Share:
12,820
ma11hew28
Author by

ma11hew28

Updated on June 25, 2022

Comments

  • ma11hew28
    ma11hew28 almost 2 years

    I want to get a pointer reference to UIKeyboard *keyboard to the keyboard on screen so that I can add a transparent subview to it, covering it completely, to achieve the effect of disabling the UIKeyboard without hiding it.

    In doing this, can I assume that there's only one UIKeyboard on the screen at a time? I.e., is it a singleton? Where's the method [UIKeyboard sharedInstance]. Brownie points if you implement that method via a category. Or, even more brownie points if you convince me why it's a bad idea to assume only one keyboard and give me a better solution.

  • ma11hew28
    ma11hew28 almost 13 years
    OK. Is there a way to disable the keyboard without hiding it?
  • RyanR
    RyanR almost 13 years
    Are you trying to prevent the keyboard from showing up when a user taps in a text field?
  • ma11hew28
    ma11hew28 almost 13 years
    No, I want to disable it during an animation. Apple's Passcode Lock feature does this when you're setting a passcode.
  • aksommerville
    aksommerville over 11 years
    The keyboard is in a separate window, so a fullscreen view to block it will not work (if you're adding it to your main window). Check [[UIApplication sharedApplication] windows].
  • The iCoder
    The iCoder over 10 years
    For generic coding please replace if condition of for loop by if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"]) { for (__strong UIView *anotherPossibleKeyboard in [possibleKeyboard subviews]) { if ([[anotherPossibleKeyboard description] hasPrefix:@"<UIKeyboard"]) { foundKeyboard = anotherPossibleKeyboard; break; } } }
  • Martin Stolz
    Martin Stolz almost 10 years
    Thanks. But I would use a slightly different check for the keyboard: Class keyboardClass = NSClassFromString(@"UIPeripheralHostView"); if ([possibleKeyboard isKindOfClass:keyboardClass]). Instead of relying on a string description. Should also be a bit more performant.
  • mihai
    mihai over 9 years
    great suggestion. it works well in 7.1. How can i find the keyboard when its off screen? or is it destroyed and doesnt exist?
  • matteok
    matteok over 7 years
    will this prevent app store approval?
  • alparsons
    alparsons over 7 years
    @matteok This alone - I don't think so. Whatever you plan to do with the keyboard though probably would. I used it for specific enterprise reasons.
  • matteok
    matteok over 7 years
    What if you apply a transform to the keyboard-window during a transition?
  • meaning-matters
    meaning-matters over 7 years
    How can beginIgnoringInteractionEvents be used?