I have tried to create a custom keyboard in iOS 8 that replaces the stock one. I really searched and could not find out if it is possible to create a keyboard with more height than the stock iOS keyboard. I replaced UIInputView but could never manage to change the height available to me.
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)
}
}
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];
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 tofalse
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;
}
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
}
}
}
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);
}
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];
}
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:
updateViewConstraints
is not called when you rotate the iPhone- Setting a
heightConstraint
produces a constraint which is ignored by the layout 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.
来源:https://stackoverflow.com/questions/24167909/ios-8-custom-keyboard-changing-the-height