iOS 8自定义键盘:改变高度



我尝试在iOS 8中创建一个自定义键盘来取代现有的键盘。我真的搜索了一下,但无法找到是否有可能创造一个比现有iOS键盘更高的键盘。我替换了UIInputView,但无法改变可用的高度。

这是我在Xcode 6.0 GM上的代码,两种方向都支持

更新:多亏了@SoftDesigner,我们现在可以消除constraint conflict警告了。

警告: XIB和storyboard未被测试。有些人报告说这对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版本:
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)
    }
}

最近,苹果更新了他们的应用程序扩展编程指南,以改变自定义键盘扩展的高度:

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

这是我发现的使高度正确更新的最小解决方案。似乎有两个关键组成部分:

  • 需要将translatesAutoresizingMaskIntoConstraints设置为false的视图添加到视图层次中。
  • 不需要在viewWillAppear之前添加高度约束。

我仍然在日志中看到Unable to simultaneously satisfy constraints错误,但无论如何它似乎都可以工作。我还看到一个跳跃,其中高度最初被设置为默认值,然后跳转到设定值。我还没有找到解决这两个问题的方法。

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)
    }
}

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
    }
}

接受的答案不适合iOS 9。我将其中的部分内容与其他一些建议结合在一起,并将其与苹果应用扩展编程指南中的代码结合在一起。

这个解决方案非常有效,因为它不会延迟修改高度直到viewDidAppear,并且在旋转时,您可以根据屏幕大小更改高度。已验证此功能在iOS 8和9中有效

几个重要的注意事项:
~ inputView中至少有一个元素需要使用自动布局
~高度约束在viewWillAppear之前不能被激活
~需要降低高度约束的priority以避免不满意的约束
~ updateViewConstraints是设置所需高度的好地方

提示:在模拟器上测试时,我发现它很脆弱,会有意想不到的行为。如果它这样对你,重置模拟器并再次运行。或者你可以禁用键盘并重新添加它。

注意:
~这个功能目前在iOS 10测试版中不支持。当它出现时,它会正确地改变高度,但如果你旋转设备,高度不会改变。这是因为updateViewConstraints在旋转时没有被触发。请提交iOS 10的bug报告。为了解决这个问题,您可以在viewDidLayoutSubviews中触发constant更改。

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
    }
}

我在iOS 8到iOS 10的自定义键盘上也遇到了类似的问题。我相信正确的解决方案是让输入视图提供一个适当的intrinsicContentSize和改变(和无效!)值,当你想改变视图的高度。示例代码:

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
        }
    }
}

其他答案没有考虑冲突约束和设备旋转。这个答案避免了诸如"无法同时满足约束"之类的错误以及由此产生的问题。它部分依赖于在未来版本的iOS中可能改变的行为,但似乎是在iOS 8上解决这个问题的唯一方法。

UIInputViewController子类中,添加这些方法:

- (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;
}

放入ViewDidAppear:

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

适用于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];
}

它为我工作

我创建这个函数工作,对我来说很好。添加prepareHeightConstraint()heightConstraint,并在updateViewConstraints和viewWillAppear调用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()
}

为了更流畅地显示更改方向的动画,我添加了以下内容:

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

我也一直在制作键盘并与高度问题作斗争。我尝试了提到的所有解决方案以及应用程序扩展编程指南解决方案,但无法修复它。我的键盘有非常复杂的视图层次。经过一番挣扎,我找到了一个完全适合我的解决方案。这是一种攻击,但我已经测试了所有的情况,即使是旋转设备,它是完美的。我想它会帮助别人,所以我把我的代码在这里。

// 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];
}

这是一个非常古老的问题,但我只是想分享,我发现UIInputViewController实际上根据你添加到它的子视图调整自己的大小。如果你像这样添加一个视图到视图控制器:

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

其中view是输入视图控制器的视图,然后指定高度约束,输入视图控制器将遵循该高度而不破坏任何约束。

这是完美的,因为您可以添加一个堆栈视图,您可以在其中添加提供固有内容大小的视图。这样就不必指定高度约束,因为堆栈视图会根据添加到它的视图隐式地调整大小。

这是不可能的。从docs

此外,不可能像iPhone上的系统键盘那样,当你点击顶部一行的一个键时,在顶部一行上方显示关键图形。

所以,如果可能的话,我们可以很容易地在第一行上面画一些东西。

编辑:

苹果似乎解决了这个问题。请参阅接受的答案

如果接受的答案对任何一个都不起作用,则使用以下方式。所有代码都是一样的,只是改变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];
}
}

最后我得到了它,将此代码块添加到您的UIInputViewController子类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)        
}

它会完美地工作…iOS 8.3

这是我为iOS9Storyboard所做的。

我使用了@skyline75489的答案并修改了它。

@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];
    }
}

在IOS 10 (swift 4)中,我不得不将上述答案结合起来,原因有三个:

    当你旋转iPhone时,不调用
  1. updateViewConstraints
  2. 设置heightConstraint会产生一个约束,该约束被布局
  3. 忽略。
  4. intrinsicContentSize只在我不理解的情况下起作用

    @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,.. 
    }
    

viewDidAppear中,我必须调用updateHeightConstraint,因为当我更改UIInputViewController

时没有调用viewWillTransition

我不需要self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false

最新更新