Search code examples
iosobjective-cios-app-extension

Dealing with changing iOS keyboard layout upon rotation


I have two slightly different layouts for my keyboard based on the orientation of the device. When I load up the keyboard at first, it looks fine. Here's the portrait version:

Portrait Keyboard

The initial constraint setup occurs within -viewDidLoad (of the UIInputViewController subclass)

portraitConstraints = [self constraintsForOrientation:UIInterfaceOrientationPortrait];
landscapeConstraints = [self constraintsForOrientation:UIInterfaceOrientationLandscapeRight];

[self.view addConstraints:portraitConstraints];
[self.view addConstraints:landscapeConstraints];

if ([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height) {
    [NSLayoutConstraint deactivateConstraints:landscapeConstraints];
} else {
    [NSLayoutConstraint deactivateConstraints:portraitConstraints];
}

This part works, as it loads correctly regardless of initial orientation. However, once I rotate the device, everything goes wrong.

Wrong

And then back to portrait:

Wrong

For reference, the proper landscape version looks like this:

Landscape

I update the constraints in -updateViewConstraints as follows:

- (void)updateViewConstraints {
    if ([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height) {
        [NSLayoutConstraint deactivateConstraints:landscapeConstraints];
        [NSLayoutConstraint activateConstraints:portraitConstraints];
    } else {
        [NSLayoutConstraint deactivateConstraints:portraitConstraints];
        [NSLayoutConstraint activateConstraints:landscapeConstraints];
    }

    [super updateViewConstraints];
}

It seems like after the view changes it suddenly takes up more than full screen instead of just the keyboard area. Any ideas how I can fix this? Thanks.


Solution

  • The issue appeared to be related to somehow implicitly overriding the height of the view that contained the keyboard. Although I didn't see anything that would force a resizing of the container view, explicitly setting the height of the container view got back to good, reproducible results.

    I got the idea of manually constraining the keyboard's height from Apple's App Extension Programming Guide: Custom Keyboard.

    Specifically, the relevant information was this:

    You can adjust the height of your custom keyboard’s primary view using Auto Layout. By default, a custom keyboard is sized to match the system keyboard, according to screen size and device orientation. A custom keyboard’s width is always set by the system to equal the current screen width. To adjust a custom keyboard’s height, change its primary view's height constraint.

    The following code lines show how you might define and add such a constraint:

    CGFloat _expandedHeight = 500;
    NSLayoutConstraint *_heightConstraint = 
    [NSLayoutConstraint constraintWithItem: self.view 
                                 attribute: NSLayoutAttributeHeight 
                                 relatedBy: NSLayoutRelationEqual 
                                    toItem: nil 
                                 attribute: NSLayoutAttributeNotAnAttribute 
                                multiplier: 0.0 
                                  constant: _expandedHeight];
    [self.view addConstraint: _heightConstraint];
    

    NOTE

    In iOS 8.0, you can adjust a custom keyboard’s height any time after its primary view initially draws on screen.

    Here is the important method in a template answer based on this information:

    - (void)updateViewConstraints {
        if (self.keyboardHeightConstraint == nil) {
            // Just starting with SOME value for the height
            self.keyboardHeightConstraint =
            [NSLayoutConstraint constraintWithItem:self.view
                                         attribute:NSLayoutAttributeHeight
                                         relatedBy:NSLayoutRelationEqual
                                            toItem:nil
                                         attribute:NSLayoutAttributeNotAnAttribute
                                        multiplier:0.0f
                                          constant:500.0f];
            [self.view addConstraint:self.keyboardHeightConstraint];
        }
        // Obviously, these values will be changed based on device AND orientation
        // These are bogus values...
        if ([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height) {
            self.keyboardHeightConstraint.constant  = 300.0f;
            [NSLayoutConstraint deactivateConstraints:self.landscapeConstraints];
            [NSLayoutConstraint activateConstraints:self.portraitConstraints];
        } else {
            self.keyboardHeightConstraint.constant  = 400.0f;
            [NSLayoutConstraint deactivateConstraints:self.portraitConstraints];
            [NSLayoutConstraint activateConstraints:self.landscapeConstraints];
        }
        [super updateViewConstraints];
    }