Ios iPhone X如何处理视图控制器inputAccessoryView?
我有一个消息传递应用程序,在全屏表格视图的底部有一个文本字段的典型UI设计。我正在将该文本字段设置为视图控制器的Ios iPhone X如何处理视图控制器inputAccessoryView?,ios,iphone,inputaccessoryview,iphone-x,Ios,Iphone,Inputaccessoryview,Iphone X,我有一个消息传递应用程序,在全屏表格视图的底部有一个文本字段的典型UI设计。我正在将该文本字段设置为视图控制器的inputAccessoryView,并调用ViewController.becomeFirstResponder(),以使该字段显示在屏幕底部 我知道这是苹果公司推荐的实现这种UI结构的方法,它在“经典”设备上运行得非常完美。但是,当我在iPhoneX模拟器上进行测试时,我注意到使用这种方法时,文本字段不尊重新的“安全区域”。文本字段呈现在主屏幕指示器下方的屏幕底部 我查看了HIG文
inputAccessoryView
,并调用ViewController.becomeFirstResponder()
,以使该字段显示在屏幕底部
我知道这是苹果公司推荐的实现这种UI结构的方法,它在“经典”设备上运行得非常完美。但是,当我在iPhoneX模拟器上进行测试时,我注意到使用这种方法时,文本字段不尊重新的“安全区域”。文本字段呈现在主屏幕指示器下方的屏幕底部
我查看了HIG文档,但没有发现任何关于视图控制器上的inputAccessoryView
的有用信息
这很困难,因为使用这种方法,我实际上并没有直接控制任何约束,我只是设置inputAccessoryView
,让视图控制器从那里处理UI。所以我不能把战场限制在新的安全区域
有没有人找到这方面的好文档,或者知道另一种在iPhoneX上运行良好的方法
我只是想将安全区域添加到inputAccessoryView(Xcode中的复选框)。并将底部空间约束更改为安全区域的底部,而不是inputAccessoryView根视图底部
这似乎是一个iOS错误,它有一个rdar问题:
我想这应该在iPhone X出现时在iOS更新中修复。这是iPhone X上InputAccessoryView的一个普遍问题。 inputAccessoryView忽略其窗口的安全区域布局指南 要解决此问题,我们必须在视图移动到其窗口时在类中手动添加约束:
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
}
}
}
注:self在这里指的是inputAccessoryView
我在这里详细描述了这一点:在iOS自动引导安全区域插入之前,简单的解决方法是将附件包装在容器视图中,并在accesory视图和容器视图之间设置底部空间约束,以匹配窗口的安全区域插入 注意:当然,当iOS update修复附件视图的底部间距时,此解决方法可以使附件视图的底部间距加倍 例如 iPhone X上的inputAccessoryView和安全区域
- 当键盘不可见时,
固定在屏幕的最底部。这是没有办法的,我认为这是故意的行为inputAccessoryView
- 视图的
(iOS 9+)和layoutMarginsGuide
(iOS 11)属性设置为safeAreaLayoutGuide
,这两个属性都尊重安全区域,即在iPhone X上:inputAccessoryView
- 当键盘不可见时,
将显示主页按钮区域bottomAnchor
- 显示键盘时,
位于bottomAnchor
的底部,这样它就不会在键盘上方留下无用的空间inputAccessoryView
- 当键盘不可见时,
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
}
}
我刚刚创建了一个名为CocoaPod的快速CocoaPod来解决这个问题。它还使用自动布局约束动态设置包裹视图的高度,因此您不必手动设置框架。支持iOS 9+ 以下是如何使用它:
安全区域InputAccessoryViewRapperView(for:)
包装任何UIView/UIButton/UILabel/etc:
let button = UIButton(type: .system)
lazy var wrappedButton: SafeAreaInputAccessoryViewWrapperView = {
return SafeAreaInputAccessoryViewWrapperView(for: button)
}()
inputAccessoryView
中的引用:
override var inputAccessoryView: UIView? {
return wrappedButton
}
inputAccessoryView
:
override var canBecomeFirstResponder: Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
becomeFirstResponder()
}
祝你好运 在Xib中,在设计的底部找到一个正确的约束,并将项目设置为
安全区域,而不是Superview
:
override var inputAccessoryView: UIView? {
return wrappedButton
}
之前:
修复:
之后:
--适用于使用JSQMessagesViewController库的用户--
我建议基于JSQ最新的develope
branch提交的固定fork
它使用的是didMoveToWindow
解决方案(我相信来自@jki?)。这并不理想,但在等待苹果公司关于inputAccessoryView
的安全区域布局指南附件或其他更好的解决方案的答案时值得一试
您可以将其添加到Podfile中,以替换以前的JSQ行:
pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true
来自代码(Swift 4)。Idea-监视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
}
override var intrinsicContentSize: CGSize {
return .zero
}
和重写intrinsicContentSize
:
convenience init() {
self.init(frame: .zero)
autoresizingMask = .flexibleHeight
}
override var intrinsicContentSize: CGSize {
return .zero
}
在nib
中,将第一个底部约束(应位于安全区域上方的视图)设置为safeArea
,将第二个底部约束设置为superview
,优先级较低
,以便在较旧的iOS上可以满足
只需为JSQMessageInputToolbar添加一个扩展
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
}
}
}
}
重复:我刚刚创建了一个支持iPhone X的应用程序。它尊重新的安全区域布局指南。使用:
autoresizingMask = [.flexibleHeight]
截图:
最简单的答案(只需一行代码)
只需使用从UIToolbar
继承的自定义视图,而不是UIView
作为inputAccessoryView
另外,不要忘记将自定义视图的自动调整大小掩码设置为uiviewsautoresizingflexibleheight
就这样。稍后感谢我。在没有解决办法的情况下对我有效的解决方案:
我正在使用UIInputViewController
通过覆盖inputAccessoryViewController
属性而不是inputAccessoryV来提供输入附件视图