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

sdt*_*eri 34 keyboard ios8 ios-app-extension

我试图在iOS 8中创建一个替换库存的自定义键盘.我真的搜索过,无法找出是否有可能创建一个比iOS iOS键盘更高的键盘.我替换了UIInputView,但永远无法改变我可用的高度.

sky*_*489 45

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

更新:感谢@SoftDesigner,我们constraint conflict现在可以消除警告.

警告:未测试XIB和故事板.据一些人报道,这不适用于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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在`viewDidLoad`中创建`heightConstraint`后,我们可以放置`heightConstraint!.priority = 999.0`来解决与标准UIView-Encapsulated-Layout-Height约束的冲突.结果 - 没有警告和高度改变(iOS8.3模拟器) (5认同)
  • 这里只是想强调一下,因为花了很长时间才弄清楚它为什么不能为我工作并为其他人工作:你必须在视图控制器的self.view中至少有一个基于自动布局的子视图,或者键盘高度根本不会改变.我选择使用基于帧的布局来提高所有键的性能,但需要一个"黑客"视图. (4认同)
  • 我只想添加 - 似乎如果从模板项目中删除"nextKeyboardButton",它就会停止工作.我不得不把它留在里面并将其设置为隐藏.一个黑客,但它似乎使它工作. (3认同)

imD*_*per 19

最近,Apple更新了他们的App扩展编程指南,以更改自定义键盘扩展的高度:

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];
Run Code Online (Sandbox Code Playgroud)

  • `viewDidAppear`可能是一个放置它的好地方. (11认同)
  • 我看到了这一点,并且自从最新发布以来我一直在尝试任何方式,但没有成功.高度仍然相同. (6认同)
  • 它对我有用.Xcode 6 beta 5.我可以使用按钮自由调整键盘的大小. (2认同)
  • 当我放入viewDidAppear时为我工作 - 谢谢你@Archagon! (2认同)

Chr*_*lli 12

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

  • 需要将视图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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 为了摆脱日志中不可满足的约束,只需将 `self.heightConstraint` 的 `priority` 设置为 `999`。 (2认同)

Jor*_*n H 7

接受的答案不适用于iOS 9.我将其中的部分内容和其他一些建议与Apple 扩展编程指南中的Apple代码结合在一起.

此解决方案效果很好,因为它不会延迟修改高度viewDidAppear,并且在旋转时您可以根据需要根据屏幕大小更改高度.已经过验证,适用于iOS 8和9.

一些重要的注意事项:
〜至少有一个元素inputView需要使用自动布局
〜高度约束直到viewWillAppear
priority需要降低高度约束以避免不满足的约束
updateViewConstraints是设置所需高度的好地方

提示:
〜在模拟器上进行测试时,我发现它非常脆弱,会出现意外情况.如果它对您执行此操作,请重置模拟器并再次运行.或者您可以只禁用键盘并再次添加.

注意:
〜这目前不适用于iOS 10测试版.当它出现时它会正确地改变高度,但如果你旋转设备,高度不会改变.这是因为updateViewConstraints旋转时不会触发.请提交针对iOS 10的错误报告.要解决此问题,您可以触发constant更改viewDidLayoutSubviews.

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
    }
}
Run Code Online (Sandbox Code Playgroud)


MrM*_*age 6

我在调整从 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)