iOS 8 Custom Keyboard: Changing the Height

烂漫一生 提交于 2019-11-26 11:39:01
skyline75489

This is my code on Xcode 6.0 GM. Both orientations are supported.

Update: Thanks to @SoftDesigner, we can eliminate the constraint conflict warning now.

Warning: XIB and storyboard are not tested. It's been reported by some folks that this does NOT work with XIB.

KeyboardViewController.h

#import <UIKit/UIKit.h>

@interface KeyboardViewController : UIInputViewController

@property (nonatomic) CGFloat portraitHeight;
@property (nonatomic) CGFloat landscapeHeight;
@property (nonatomic) BOOL isLandscape;
@property (nonatomic) NSLayoutConstraint *heightConstraint;
@property (nonatomic) UIButton *nextKeyboardButton;

@end

KeyboardViewController.m

#import "KeyboardViewController.h"

@interface KeyboardViewController ()
@end

@implementation KeyboardViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Perform custom initialization work here
        self.portraitHeight = 256;
        self.landscapeHeight = 203;
    }
    return self;
}

- (void)updateViewConstraints {
    [super updateViewConstraints];
    // Add custom view sizing constraints here
    if (self.view.frame.size.width == 0 || self.view.frame.size.height == 0)
        return;

    [self.inputView removeConstraint:self.heightConstraint];
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
    CGFloat screenH = screenSize.height;
    CGFloat screenW = screenSize.width;
    BOOL isLandscape =  !(self.view.frame.size.width ==
                      (screenW*(screenW<screenH))+(screenH*(screenW>screenH)));
    NSLog(isLandscape ? @"Screen: Landscape" : @"Screen: Potriaint");
    self.isLandscape = isLandscape;
    if (isLandscape) {
        self.heightConstraint.constant = self.landscapeHeight;
        [self.inputView addConstraint:self.heightConstraint];
    } else {
        self.heightConstraint.constant = self.portraitHeight;
        [self.inputView addConstraint:self.heightConstraint];
    }
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // Perform custom UI setup here
    self.nextKeyboardButton = [UIButton buttonWithType:UIButtonTypeSystem];

    [self.nextKeyboardButton setTitle:NSLocalizedString(@"Next Keyboard", @"Title for 'Next Keyboard' button") forState:UIControlStateNormal];
    [self.nextKeyboardButton sizeToFit];
    self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = NO;

    [self.nextKeyboardButton addTarget:self action:@selector(advanceToNextInputMode) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:self.nextKeyboardButton];

    NSLayoutConstraint *nextKeyboardButtonLeftSideConstraint = [NSLayoutConstraint constraintWithItem:self.nextKeyboardButton attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0];
    NSLayoutConstraint *nextKeyboardButtonBottomConstraint = [NSLayoutConstraint constraintWithItem:self.nextKeyboardButton attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
    [self.view addConstraints:@[nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint]];


    self.heightConstraint = [NSLayoutConstraint constraintWithItem:self.inputView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:self.portraitHeight];

    self.heightConstraint.priority = UILayoutPriorityRequired - 1; // This will eliminate the constraint conflict warning.

}

- (void)textWillChange:(id<UITextInput>)textInput {
    // The app is about to change the document's contents. Perform any preparation here.
}

- (void)textDidChange:(id<UITextInput>)textInput {
}

@end

Swift 1.0 version:

class KeyboardViewController: UIInputViewController {

    @IBOutlet var nextKeyboardButton: UIButton!

    let portraitHeight:CGFloat = 256.0
    let landscapeHeight:CGFloat = 203.0
    var heightConstraint: NSLayoutConstraint?
    override func updateViewConstraints() {
        super.updateViewConstraints()
        // Add custom view sizing constraints here
        if (self.view.frame.size.width == 0 || self.view.frame.size.height == 0) {
            return
        }
        inputView.removeConstraint(heightConstraint!)
        let screenSize = UIScreen.mainScreen().bounds.size
        let screenH = screenSize.height;
        let screenW = screenSize.width;
        let isLandscape =  !(self.view.frame.size.width == screenW * ((screenW < screenH) ? 1 : 0) + screenH * ((screenW > screenH) ? 1 : 0))
        NSLog(isLandscape ? "Screen: Landscape" : "Screen: Potriaint");
        if (isLandscape) {
            heightConstraint!.constant = landscapeHeight;
            inputView.addConstraint(heightConstraint!)
        } else {
            heightConstraint!.constant = self.portraitHeight;
            inputView.addConstraint(heightConstraint!)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Perform custom UI setup here
        self.nextKeyboardButton = UIButton.buttonWithType(.System) as UIButton

        self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), forState: .Normal)
        self.nextKeyboardButton.sizeToFit()
    self.nextKeyboardButton.setTranslatesAutoresizingMaskIntoConstraints(false)

        self.nextKeyboardButton.addTarget(self, action: "advanceToNextInputMode", forControlEvents: .TouchUpInside)

        self.view.addSubview(self.nextKeyboardButton)

        var nextKeyboardButtonLeftSideConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 0.0)
        var nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
        self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint])

        heightConstraint = NSLayoutConstraint(item: self.inputView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: portraitHeight)
        heightConstraint!.priority = 999.0
    }

    override func textWillChange(textInput: UITextInput) {
        // The app is about to change the document's contents. Perform any preparation here.
    }

    override func textDidChange(textInput: UITextInput) {
        // The app has just changed the document's contents, the document context has been updated.

        var textColor: UIColor
        var proxy = self.textDocumentProxy as UITextDocumentProxy
        if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
            textColor = UIColor.whiteColor()
        } else {
            textColor = UIColor.blackColor()
        }
        self.nextKeyboardButton.setTitleColor(textColor, forState: .Normal)
    }
}
imDeveloper

Recently, Apple has updated their App extension programming guide to change the height of custom keyboard extension:

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];
Chris Vasselli

This is the minimal solution I've found to cause the height to get updated properly. There seems to be two key components:

  • A view with translatesAutoresizingMaskIntoConstraints set to false needs to be added to the view hierarchy.
  • The height constraint needs to be added no earlier than viewWillAppear.

I'm still seeing an Unable to simultaneously satisfy constraints error in the log, but it seems to work OK anyway. I'm also still seeing a jump where the height is initially set to its default value, and then jumps to the set value. I haven't yet figured out any way around either of these problems.

import UIKit

class KeyboardViewController: UIInputViewController {

    var heightConstraint: NSLayoutConstraint!

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.inputView.addConstraint(self.heightConstraint)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let dummyView = UILabel(frame:CGRectZero)
        dummyView.setTranslatesAutoresizingMaskIntoConstraints(false)
        self.view.addSubview(dummyView);

        let height : CGFloat = 400

        self.heightConstraint = NSLayoutConstraint( item:self.inputView, attribute:.Height, relatedBy:.Equal, toItem:nil, attribute:.NotAnAttribute, multiplier:0.0, constant:height)
    }
}

Update for Swift 4:

import UIKit

class KeyboardViewController: UIInputViewController
{
    private weak var _heightConstraint: NSLayoutConstraint?

    override func viewWillAppear(_ animated: Bool)
    {
        super.viewWillAppear(animated)

        guard nil == _heightConstraint else { return }

        // We must add a subview with an `instrinsicContentSize` that uses autolayout to force the height constraint to be recognized.
        //
        let emptyView = UILabel(frame: .zero)
        emptyView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(emptyView);

        let heightConstraint = NSLayoutConstraint(item: view,
                                                  attribute: .height,
                                                  relatedBy: .equal,
                                                  toItem: nil,
                                                  attribute: .notAnAttribute,
                                                  multiplier: 0.0,
                                                  constant: 240)
        heightConstraint.priority = .required - 1
        view.addConstraint(heightConstraint)
        _heightConstraint = heightConstraint
    }
}

The accepted answer wasn't working for iOS 9. I combined pieces of it and some other suggestions here along with Apple's code in the App Extension Programming Guide.

This solution works great as it doesn't delay modifying the height until viewDidAppear, and upon rotation you can change the height if desired based on the screen size. Verified this works in iOS 8 and 9.

A few important notes:
~ At least one element in the inputView needs to use Auto Layout
~ The height constraint cannot be activated until viewWillAppear
~ The priority of the height constraint needs to be lowered to avoid unsatisfiable constraints
~ updateViewConstraints is a good place to set the desired height

Tips:
~ When testing on the simulator, I found it to be very flaky and would behave unexpectedly. If it does this to you, reset the simulator and run again. Or you may be able to just disable the keyboard and add it again.

Note:
~ This is not currently working in iOS 10 beta. It will properly change the height when it appears, but if you rotate the device the height will not change. This is because updateViewConstraints is not triggered on rotate. Please file a bug report against iOS 10. To workaround the issue, you can trigger the constant change in viewDidLayoutSubviews instead.

var nextKeyboardButton: UIButton!
var heightConstraint: NSLayoutConstraint?


override func viewDidLoad() {
    super.viewDidLoad()

    self.nextKeyboardButton = UIButton(type: .System)

    self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), forState: .Normal)
    self.nextKeyboardButton.sizeToFit()
    self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false

    self.nextKeyboardButton.addTarget(self, action: "advanceToNextInputMode", forControlEvents: .TouchUpInside)

    self.view.addSubview(self.nextKeyboardButton)

    let nextKeyboardButtonLeftSideConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1, constant: 0)
    let nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0)
    NSLayoutConstraint.activateConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint])
}

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    self.heightConstraint = NSLayoutConstraint(item:self.inputView!, attribute:.Height, relatedBy:.Equal, toItem:nil, attribute:.NotAnAttribute, multiplier:0, constant:0)
    self.heightConstraint!.priority = 999
    self.heightConstraint!.active = true
}

override func updateViewConstraints() {
    super.updateViewConstraints()

    guard self.heightConstraint != nil && self.view.frame.size.width != 0 && self.view.frame.size.height != 0 else { return }

    let portraitHeight: CGFloat = 400
    let landscapeHeight: CGFloat = 200
    let screenSize = UIScreen.mainScreen().bounds.size

    let newHeight = screenSize.width > screenSize.height ? landscapeHeight : portraitHeight

    if (self.heightConstraint!.constant != newHeight) {
        self.heightConstraint!.constant = newHeight
    }
}

Other answers do not take into account conflicting constraints and device rotation. This answer avoids errors like "Unable to simultaneously satisfy constraints" and the problems that result from it. It partly relies on behaviors that can change in future versions of iOS, but seems to be the only way to solve this problem on iOS 8.

In your UIInputViewController subclass, add these methods:

- (void)updateViewConstraints {
    [super updateViewConstraints];
    // Update height when appearing
    [self updateViewHeightConstraintIfNeeded];
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    // Update height when rotating
    [self updateViewHeightConstraintIfNeeded];
}

- (void)updateViewHeightConstraintIfNeeded {
    CGFloat preferedHeight = 216; // Portrait
    if ( [UIScreen mainScreen].bounds.size.width
         > [UIScreen mainScreen].bounds.size.height ) {
        // Landscape
        preferedHeight = 162;
    }
    NSLayoutConstraint *constraint = [self findViewHeightConstraint];
    if ( preferedHeight != constraint.constant ) {
        if ( constraint ) {
            constraint.constant = preferedHeight;
        } else {
            // This is not run on current versions of iOS, but we add it to
            // make sure the constraint exits
            constraint = [NSLayoutConstraint constraintWithItem:self.view
                          attribute:NSLayoutAttributeHeight
                          relatedBy:NSLayoutRelationEqual
                             toItem:nil
                          attribute:NSLayoutAttributeNotAnAttribute
                         multiplier:0
                           constant:preferedHeight];
            [self.view.superview addConstraint:constraint];
        }
    }
}

- (NSLayoutConstraint*)findViewHeightConstraint {
    NSArray *constraints = self.view.superview.constraints;
    for ( NSLayoutConstraint *constraint in constraints ) {
        if ( constraint.firstItem == self.view
             && constraint.firstAttribute == NSLayoutAttributeHeight )
            return constraint;
    }
    return nil;
}
MrMage

I had similar issues with sizing a custom keyboard from iOS 8 to iOS 10. I believe the proper solution is to have the input view provide a proper intrinsicContentSize and change (and invalidate!) that value when you want to change the view's height. Sample code:

class CustomInputView: UIInputView {
    var intrinsicHeight: CGFloat = 200 {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }

    init() {
        super.init(frame: CGRect(), inputViewStyle: .keyboard)
        self.translatesAutoresizingMaskIntoConstraints = false
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.translatesAutoresizingMaskIntoConstraints = false
    }

    override var intrinsicContentSize: CGSize {
        return CGSize(width: UIViewNoIntrinsicMetric, height: self.intrinsicHeight)
    }
}

class ViewController: UIViewController {
    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        textView.becomeFirstResponder()

        let inputView = CustomInputView()
        // To make the view's size more clear.
        inputView.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0.5, alpha: 1)
        textView.inputView = inputView

        // To demonstrate a change to the view's intrinsic height.
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(2)) {
            inputView.intrinsicHeight = 400
        }
    }
}
Jeet

Put this in ViewDidAppear:

NSLayoutConstraint *heightConstraint = 
                            [NSLayoutConstraint constraintWithItem: self.view
                                 attribute: NSLayoutAttributeHeight
                                 relatedBy: NSLayoutRelationEqual
                                    toItem: nil
                                 attribute: NSLayoutAttributeNotAnAttribute
                                multiplier: 0.0
                                  constant: 300];
    [self.view addConstraint: heightConstraint];

Works in iOS 8.1

- (void)updateViewConstraints {


[super updateViewConstraints];

// Add custom view sizing constraints here
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];
}   
 -(void)viewDidAppear:(BOOL)animated
{
    [self updateViewConstraints];
}

It work for me

I create this fonction work, fine for me. Add prepareHeightConstraint() and heightConstraint and in your updateViewConstraints and viewWillAppear call prepareHeightConstraint()

    private var heightConstraint: NSLayoutConstraint!

    /**
        Prepare the height Constraint when create or change orientation keyboard
    */
    private func prepareHeightConstraint() {

        guard self.heightConstraint != nil else {
            let dummyView = UILabel(frame:CGRectZero)
            dummyView.translatesAutoresizingMaskIntoConstraints = false
            self.view.addSubview(dummyView)

            self.heightConstraint = NSLayoutConstraint( item:self.view, attribute:.Height, relatedBy:.Equal, toItem:nil, attribute:.NotAnAttribute, multiplier:0.0, constant: /* Here your height */)
            // /* Here your height */ Here is when your create your keyboard

            self.heightConstraint.priority = 750
            self.view.addConstraint(self.heightConstraint!)
            return
        }

        // Update when change orientation etc..
        self.heightConstraint.constant = /* Here your height */ 

    }


    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        // When keyboard is create
        self.prepareHeightConstraint()
    }


    override func updateViewConstraints() {
        super.updateViewConstraints()
        guard let viewKeyboard = self.inputView where viewKeyboard.frame.size.width != 0 && viewKeyboard.frame.size.width != 0 {
            return
        }
        //Update change orientation, update just the constant
        self.prepareHeightConstraint()
}

For more smoothly animation on change orientation I add this:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    self.view.window.frame = CGRectMake(0, 0, width, heigth);
}
ranvijay singh

I have been also making a Keyboard and struggling with height issue. I tried all the solutions mentioned and also the App extension programming guide solution but could't fix it. My keyboard has very complex view hierarchy. After struggling i found a solution that is completely working for me. It's a kind of hack but i have tested all the scenarios even with rotation of device and it's perfect. I thought it will help someone so i am putting my code here..

// Keep this code inside the UIInputViewController

@implementation KeyBoardViewController

@property (strong, nonatomic) NSLayoutConstraint *heightConstraint;

// This method will first get the height constraint created by (Run time system or OS) then deactivate it and add our own custom height constraint.

(void)addHeightConstraint {
    for (NSLayoutConstraint* ct in self.view.superview.constraints) {
        if (ct.firstAttribute == NSLayoutAttributeHeight) {
            [NSLayoutConstraint deactivateConstraints:@[ct]];
        }
    }
    if (!_heightConstraint) {
        _heightConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant: 300];
        [_heightConstraint setPriority:UILayoutPriorityDefaultHigh];
        [self.view addConstraint:_heightConstraint];
    }else {
        _heightConstraint.constant = 300;
    }

    if (_heightConstraint && !_heightConstraint.isActive) {
        [NSLayoutConstraint activateConstraints:@[_heightConstraint]];
    }
    [self.view layoutIfNeeded];
}


(void)viewWillLayoutSubviews {
    [self addHeightConstraint];
}
sha256

It's not possible. From the docs

In addition, it is not possible to display key artwork above the top row, as the system keyboard does on iPhone when you tap a key in the top row.`

So, if it were possible, we could easily draw something above the top row.

Edit:

It seems Apple fixed this thing. Please see the accepted Answer

If accepted answer is not working for any one then use below way.all code will be same only change the code inside updateViewConstraints Reference

- (void)updateViewConstraints {
   [super updateViewConstraints];
   if (self.view.frame.size.width == 0 || self.view.frame.size.height == 0)
    return;
  [self.inputView removeConstraint:self.heightConstraint];
  CGSize screenSize = [[UIScreen mainScreen] bounds].size;
  CGFloat screenH = screenSize.height;
  CGFloat screenW = screenSize.width;
  BOOL isLandscape =  !(self.view.frame.size.width ==
                      (screenW*(screenW<screenH))+(screenH*(screenW>screenH)));
   NSLog(isLandscape ? @"Screen: Landscape" : @"Screen: Potriaint");
 if (isLandscape)
 {
    self.heightConstraint = [NSLayoutConstraint constraintWithItem:self.inputView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant: self.landscapeHeight];
    [self.inputView addConstraint:self.heightConstraint];
  } else {
    self.heightConstraint = [NSLayoutConstraint constraintWithItem:self.inputView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant: self.portraitHeight];
    [self.inputView addConstraint:self.heightConstraint];
}

}

Finally i got it, add this code block to your UIInputViewController subclass Class :

override func viewDidAppear(animated: Bool) {
      let desiredHeight:CGFloat = 300.0 // or anything you want
      let heightConstraint = NSLayoutConstraint(item: view,  attribute:NSLayoutAttribute.Height, 
relatedBy: NSLayoutRelation.Equal,
 toItem: nil, 
attribute: NSLayoutAttribute.NotAnAttribute, 
multiplier: 1.0, 
constant: desiredHeight)

view.addConstraint(heightConstraint)        
}

It will work perfectly .. iOS 8.3

This is what I've done for iOS9 and Storyboard.

I have used @skyline75489's (big thanks) answer and modified it.

@property (nonatomic) CGFloat portraitHeight;
@property (nonatomic) CGFloat landscapeHeight;
@property (nonatomic) BOOL isLandscape;
@property (nonatomic) NSLayoutConstraint *heightConstraint;

@property (nonatomic) BOOL viewWillAppearExecuted;


- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // Perform custom initialization work here
        self.portraitHeight = 256;
        self.landscapeHeight = 203;
    }
    return self;
}

- (void)updateViewConstraints {
    [super updateViewConstraints];

    if (_viewWillAppearExecuted)
        [self adjustHeight];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.view addConstraint:self.heightConstraint];
    _viewWillAppearExecuted = YES;
}

#pragma mark - Setters/Getters

- (NSLayoutConstraint *)heightConstraint
{
    if (!_heightConstraint) {
        _heightConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:self.portraitHeight];
        _heightConstraint.priority = UILayoutPriorityRequired - 1;
    }

    return _heightConstraint;
}

#pragma mark - Methods

- (void)adjustHeight
{
    if (self.view.frame.size.width == 0 || self.view.frame.size.height == 0)
        return;

    [self.view removeConstraint:self.heightConstraint];
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
    CGFloat screenH = screenSize.height;
    CGFloat screenW = screenSize.width;
    BOOL isLandscape =  !(self.view.frame.size.width ==
                          (screenW*(screenW<screenH))+(screenH*(screenW>screenH)));

    self.isLandscape = isLandscape;
    if (isLandscape) {
        self.heightConstraint.constant = self.landscapeHeight;
        [self.view addConstraint:self.heightConstraint];
    } else {
        self.heightConstraint.constant = self.portraitHeight;
        [self.view addConstraint:self.heightConstraint];
    }
}

In IOS 10 (swift 4) I had to combine the above answers because of three reasons:

  1. updateViewConstraints is not called when you rotate the iPhone
  2. Setting a heightConstraint produces a constraint which is ignored by the layout
  3. intrinsicContentSize did function only under circumstances, which I did not understand

    @objc public class CustomInputView: UIInputView {
        var intrinsicHeight: CGFloat = 296.0 {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
      }
      @objc public init() {
        super.init(frame: CGRect(), inputViewStyle: .keyboard)
        self.translatesAutoresizingMaskIntoConstraints = false
      }
      @objc public required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.translatesAutoresizingMaskIntoConstraints = false
      }
      @objc public override var intrinsicContentSize: CGSize {
        let screenSize = UIScreen.main.bounds.size
        let newHeight :CGFloat = screenSize.width > screenSize.height ? 230.0 : intrinsicHeight
        return CGSize(width: UIViewNoIntrinsicMetric, height: newHeight)
      }
    }
    
    @objc public class KeyboardViewController: UIInputViewController {
      let portraitHeight:CGFloat = 296.0
      let landscapeHeight:CGFloat = 230.0
      var heightConstraint: NSLayoutConstraint?
      func updateHeightConstraint(to size: CGSize){
        var heightConstant=portraitHeight
        if size.width>400 {
            heightConstant=landscapeHeight
        }
        if heightConstant != heightConstraint!.constant {
            inputView?.removeConstraint(heightConstraint!)
            heightConstraint!.constant = heightConstant;
            inputView?.addConstraint(heightConstraint!)
        }
      }
      override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        updateHeightConstraint(to: size)
      }
      override public func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        updateHeightConstraint(to: UIScreen.main.bounds.size)
      }
      override public func viewDidLoad() {
        super.viewDidLoad()
        heightConstraint = NSLayoutConstraint(item: self.inputView as Any, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1.0, constant: portraitHeight)
        heightConstraint!.priority = UILayoutPriority(rawValue: 999.0)
        heightConstraint!.isActive=true;
      }
    //... code to insert, delete,.. 
    }
    

in viewDidAppear I had to call updateHeightConstraint because the viewWillTransition was not called when I changed the UIInputViewController

I did not need self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false

This is a very old question, but I just wanted to share that I found out that UIInputViewController actually resizes itself depending on the subviews you add to it. So if you add a view like this to your view controller:

let myView: UIView(frame: .zero)
myView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

where view is the input view controller's view, then specifies a height constraint, the input view controller will honor that height without breaking any constraints.

This is perfect, since you could add a stack view, to which you could add views that provide an intrinsic content size. You would then not have to specify a height constraint, since the stack view would be implicitly resized depending on the views you add to it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!