Search code examples
iosobjective-ckeyboardios-keyboard-extension

How to present iOS Keyboard extension using presentViewController


My goal is to present the iOS Keyboard with the method presentViewController:animated:completion whenever the keyboard has successfully instantiated, taking advantage of the smooth upward animation.

Some very brief background: the keyboard extension project is written in Objective-C, with KeyboardViewController.h/.m handling the logic and some basic view layout from a inputView.xib.

Based on this SO question, several of the the answers suggested using the approach of calling presentViewController from [UIApplication sharedApplication].keyWindow.rootViewController. The problem is, as this is an iOS extension, when I try to replicate this method, I get the error that sharedApplication is not available. I was wondering if a workaround exists where I could present the Keyboard with the method presentViewController, either somehow via itself or via a super? In my current attempts, calling [self presentViewController: self...] causes an exception.

Much appreciated!


Solution

  • It can't be done.

    One can actually acquire a reference to [UIApplication sharedApplication] easily, the most straightforward (though liable to get you rejected in App Store review) would be:

      Class appClass = NSClassFromString(@"UIApplication");
      id app = [appClass performSelector:@selector(sharedApplication)];
    

    Running on the iPhone 5 simulator, [app keyWindow] returns nil when called from the principal input view controller's viewDidAppear: method.

    [UIApplication windows], however, does not, so we could try

    [[[app windows].firstObject rootViewController] presentViewController:self
                                                                       animated:YES
                                                                     completion:nil];
    

    ...but it just throws an exception:

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller .'

    Walking the view hierarchy starting from UIApplication windows] reveals an interesting fact: the window of our UIInputViewController isn't in this window hierarchy. self.view.window returns a separate _UIHostedWindow with no superView.

    Furthermore, There is no variation across apps of the addresses in memory of windows returned from [UIApplication sharedApplication].

    Conclusion: the UIApplication we get from sharedApplication is the one for our process, not for the host process. Presenting on views and windows it owns therefore is pointless, because its views aren't visible to the user.