iPhone X如何处理View Controller inputAccessoryView?

我有一个消息传递应用程序,它具有全屏表格视图底部的文本字段的典型UI设计。 我将该文本字段设置为视图控制器的inputAccessoryView并调用ViewController.becomeFirstResponder()以使字段显示在屏幕底部。

我知道这是Apple推荐的实现这种UI结构的方式,它在“经典”设备上运行得很好但是当我在iPhone X模拟器上测试时,我注意到使用这种方法,文本字段不尊重新的“安全区域” 。 文本字段呈现在主屏幕指示器下方屏幕的最底部。

我查看了HIG文档,但没有找到任何关于视图控制器上的inputAccessoryView有用信息。

这很难,因为使用这种方法我实际上并没有直接控制任何约束,我只是设置inputAccessoryView并让视图控制器从那里处理UI。 所以我不能仅仅把这个领域限制在新的安全区域。

有没有人在这方面找到了很好的文档,或者知道在iPhone X上运行良好的替代方法?

在此处输入图像描述

iPhone X上的inputAccessoryView和安全区域

  • 当键盘不可见时, inputAccessoryView固定在屏幕的最底部。 没有办法解决这个问题,我认为这是预期的行为。

  • 设置为inputAccessoryView的视图的layoutMarginsGuide (iOS 9+)和safeAreaLayoutGuide (iOS 11)属性都尊重安全区域,即在iPhone X上:

    • 当键盘不可见时, bottomAnchor主页按钮区域
    • 当键盘显示时, bottomAnchor位于bottomAnchor的底部,因此它不会在键盘上方留下任何无用的空间

工作范例:

 import UIKit class ViewController: UIViewController { override var canBecomeFirstResponder: Bool { return true } var _inputAccessoryView: UIView! override var inputAccessoryView: UIView? { if _inputAccessoryView == nil { _inputAccessoryView = CustomView() _inputAccessoryView.backgroundColor = UIColor.groupTableViewBackground let textField = UITextField() textField.borderStyle = .roundedRect _inputAccessoryView.addSubview(textField) _inputAccessoryView.autoresizingMask = .flexibleHeight textField.translatesAutoresizingMaskIntoConstraints = false textField.leadingAnchor.constraint( equalTo: _inputAccessoryView.leadingAnchor, constant: 8 ).isActive = true textField.trailingAnchor.constraint( equalTo: _inputAccessoryView.trailingAnchor, constant: -8 ).isActive = true textField.topAnchor.constraint( equalTo: _inputAccessoryView.topAnchor, constant: 8 ).isActive = true // this is the important part : textField.bottomAnchor.constraint( equalTo: _inputAccessoryView.layoutMarginsGuide.bottomAnchor, constant: -8 ).isActive = true } return _inputAccessoryView } override func loadView() { let tableView = UITableView() tableView.keyboardDismissMode = .interactive view = tableView } } class CustomView: UIView { // this is needed so that the inputAccesoryView is properly sized from the auto layout constraints // actual value is not important override var intrinsicContentSize: CGSize { return CGSize.zero } } 

在这里查看结果

这是iPhone X上的inputAccessoryViews的一般问题.inputAccessoryView忽略其窗口的safeAreaLayoutGuides。

要修复它,您必须在视图移动到其窗口时手动在类中添加约束:

 override func didMoveToWindow() { super.didMoveToWindow() if #available(iOS 11.0, *) { if let window = self.window { self.bottomAnchor.constraintLessThanOrEqualToSystemSpacingBelow(window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0).isActive = true } } } 

PS:self这里指的是inputAccessoryView。

我在这里详细介绍了它: http : //ahbou.org/post/165762292157/iphone-x-inputaccessoryview-fix

在Xib中,在设计的底部找到正确的约束,并将项目设置为Safe Area而不是Superview

之前在此处输入图像描述

修复在此处输入图像描述

之后在此处输入图像描述

我刚刚创建了一个名为SafeAreaInputAccessoryViewWrapperView的快速CocoaPod来解决这个问题。 它还使用自动布局约束动态设置包装视图的高度,因此您不必手动设置框架。 支持iOS 9+。

以下是如何使用它:

  1. 使用SafeAreaInputAccessoryViewWrapperView(for:)包装任何UIView / UIButton / UILabel / etc:

     SafeAreaInputAccessoryViewWrapperView(for: button) 
  2. 在您class级的某个地方存储对此的引用:

     let button = UIButton(type: .system) lazy var wrappedButton: SafeAreaInputAccessoryViewWrapperView = { return SafeAreaInputAccessoryViewWrapperView(for: button) }() 
  3. inputAccessoryView返回引用:

     override var inputAccessoryView: UIView? { return wrappedButton } 
  4. (可选)即使键盘关闭,也始终显示inputAccessoryView

     override var canBecomeFirstResponder: Bool { return true } override func viewDidLoad() { super.viewDidLoad() becomeFirstResponder() } 

祝你好运!

似乎这是一个iOS错误,并且有一个问题: inputAccessoryViews应该尊重iPhone X上外部键盘的安全区域插入

我想这应该在iOS更新时修复,当iPhone X出现时。

直到安全是插件由iOS自动引导,简单的解决方法是将您的配件包装在容器视图中,并在配件视图和容器视图之间设置底部空间约束以匹配窗口的安全区域插入。

注意:当iOS更新修复附件视图的底部间距时,此解决方法当然可以使配件视图间距从底部翻倍。

例如

 - (void) didMoveToWindow { [super didMoveToWindow]; if (@available(iOS 11.0, *)) { self.bottomSpaceConstraint.constant = self.window.safeAreaInsets.bottom; } } 

只需为JSQMessagesInputToolbar添加一个扩展名

 extension JSQMessagesInputToolbar { override open func didMoveToWindow() { super.didMoveToWindow() if #available(iOS 11.0, *) { if self.window?.safeAreaLayoutGuide != nil { self.bottomAnchor.constraintLessThanOrEqualToSystemSpacingBelow((self.window?.safeAreaLayoutGuide.bottomAnchor)!, multiplier: 1.0).isActive = true } } } } 

duplicate: jsqmessageviewcontroller ios11工具栏

– 对于那些使用JSQMessagesViewController lib的人 –

我提出了一个基于JSQ最新develop分支提交的固定fork。

它使用的是didMoveToWindow解决方案(来自@jki,我相信?)。 不太理想,但值得尝试等待Apple关于inputAccessoryView的安全区域布局指南附件或任何其他更好的修复的答案。

您可以将其添加到Podfile中,替换以前的JSQ行:

 pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true 

从代码(Swift 4)。 想法 – 监视layoutMarginsDidChange事件并调整intrinsicContentSize

 public final class AutoSuggestionView: UIView { private lazy var tableView = UITableView(frame: CGRect(), style: .plain) private var bottomConstraint: NSLayoutConstraint? var streetSuggestions = [String]() { didSet { if streetSuggestions != oldValue { updateUI() } } } var handleSelected: ((String) -> Void)? public override func initializeView() { addSubview(tableView) setupUI() setupLayout() // ... updateUI() } public override var intrinsicContentSize: CGSize { let size = super.intrinsicContentSize let numRowsToShow = 3 let suggestionsHeight = tableView.rowHeight * CGFloat(min(numRowsToShow, tableView.numberOfRows(inSection: 0))) //! Explicitly used constraint instead of layoutMargins return CGSize(width: size.width, height: suggestionsHeight + (bottomConstraint?.constant ?? 0)) } public override func layoutMarginsDidChange() { super.layoutMarginsDidChange() bottomConstraint?.constant = layoutMargins.bottom invalidateIntrinsicContentSize() } } extension AutoSuggestionView { private func updateUI() { backgroundColor = streetSuggestions.isEmpty ? .clear : .white invalidateIntrinsicContentSize() tableView.reloadData() } private func setupLayout() { let constraint0 = trailingAnchor.constraint(equalTo: tableView.trailingAnchor) let constraint1 = tableView.leadingAnchor.constraint(equalTo: leadingAnchor) let constraint2 = tableView.topAnchor.constraint(equalTo: topAnchor) //! Used bottomAnchor instead of layoutMarginGuide.bottomAnchor let constraint3 = bottomAnchor.constraint(equalTo: tableView.bottomAnchor) bottomConstraint = constraint3 NSLayoutConstraint.activate([constraint0, constraint1, constraint2, constraint3]) } } 

用法:

 let autoSuggestionView = AutoSuggestionView() // ... textField.inputAccessoryView = autoSuggestionView 

结果:

在此处输入图像描述 在此处输入图像描述

如果您已经通过nib文件加载了自定义视图。

像这样添加一个方便的构造函数:

 convenience init() { self.init(frame: .zero) autoresizingMask = .flexibleHeight } 

并覆盖intrinsicContentSize

 override var intrinsicContentSize: CGSize { return .zero } 

nib将第一个底部约束(应该保持在安全区域之上的视图)设置safeArea ,将第二个约束设置为具有较低priority safeArea ,以便在较旧的iOS上满足。

我只是为inputAccessoryView添加安全区域(Xcode上的复选框)。 并且改变底部空间约束等于安全区域的底部而不是inputAccessoryView根视图底部。

约束

结果