[英]iPhone X how to handle View Controller inputAccessoryView?
我有一個消息應用程序,它具有全屏表格視圖底部文本字段的典型 UI 設計。 我將該文本字段設置為視圖控制器的inputAccessoryView
並調用ViewController.becomeFirstResponder()
以使該字段顯示在屏幕底部。
我知道這是 Apple 推薦的完成此 UI 結構的方法,它在“經典”設備上完美運行,但是當我在 iPhone X 模擬器上測試時,我注意到使用這種方法時,文本字段不尊重新的“安全區域” . 文本字段呈現在主屏幕指示器下方的屏幕最底部。
我查看了 HIG 文檔,但沒有發現任何關於視圖控制器上的inputAccessoryView
有用的信息。
這很困難,因為使用這種方法我實際上並沒有直接控制任何約束,我只是設置inputAccessoryView
並讓視圖控制器從那里處理 UI。 所以我不能只將這個領域限制在新的安全區域。
有沒有人在這方面找到好的文檔或知道在 iPhone X 上運行良好的替代方法?
inputAccessoryView
和安全區域當鍵盤不可見時, inputAccessoryView
固定在屏幕的最底部。 沒有辦法解決這個問題,我認為這是有意的行為。
設置為inputAccessoryView
的視圖的layoutMarginsGuide
(iOS 9+) 和safeAreaLayoutGuide
(iOS 11) 屬性都尊重安全區域,即在 iPhone X 上:
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
}
}
這是 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
我剛剛創建了一個名為SafeAreaInputAccessoryViewWrapperView的快速CocoaPod來解決這個問題。 它還使用自動布局約束動態設置包裝視圖的高度,因此您不必手動設置框架。 支持 iOS 9+。
以下是如何使用它:
使用SafeAreaInputAccessoryViewWrapperView(for:)
包裹任何 UIView/UIButton/UILabel/etc :
SafeAreaInputAccessoryViewWrapperView(for: button)
在你的類中的某個地方存儲對此的引用:
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() }
祝你好運!
只需為 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
}
}
}
}
似乎這是一個 iOS 錯誤,並且存在一個 rdar 問題: inputAccessoryViews 應該尊重 iPhone X 上帶有外部鍵盤的安全區域插入
我想這應該在 iPhone X 出現時在 iOS 更新中修復。
在 iOS 自動引導安全插入之前,簡單的解決方法是將您的配件包裝在容器視圖中,並在配件視圖和容器視圖之間設置底部空間約束以匹配窗口的安全區域插入。
注意:當然,當 iOS 更新修復附件視圖的底部間距時,此解決方法可以使附件視圖間距從底部增加一倍。
例如
- (void) didMoveToWindow {
[super didMoveToWindow];
if (@available(iOS 11.0, *)) {
self.bottomSpaceConstraint.constant = self.window.safeAreaInsets.bottom;
}
}
來自代碼(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
結果:
只需使用繼承自UIToolbar
而不是UIView
的自定義視圖作為inputAccessoryView
。
另外不要忘記將該自定義視圖的自動調整大小掩碼設置為UIViewAutoresizingFlexibleHeight
。
就是這樣。 以后謝謝我。
經過大量研究和試驗,這似乎是嘗試將UIToolbar
與文本輸入的inputAccessoryView
一起使用的inputAccessoryView
解決方案。 (大多數現有的解決方案是使用固定的附件視圖而不是將其分配給文本視圖(並在鍵盤關閉時隱藏它)。)
該代碼的靈感來自https://stackoverflow.com/a/46510833/2603230 。 基本上,我們首先創建一個具有工具欄子視圖的自定義視圖:
class CustomInputAccessoryWithToolbarView: UIView {
public var toolbar: UIToolbar!
override init(frame: CGRect) {
super.init(frame: frame)
// https://stackoverflow.com/a/58524360/2603230
toolbar = UIToolbar(frame: frame)
// Below is adopted from https://stackoverflow.com/a/46510833/2603230
self.addSubview(toolbar)
self.autoresizingMask = .flexibleHeight
toolbar.translatesAutoresizingMaskIntoConstraints = false
toolbar.leadingAnchor.constraint(
equalTo: self.leadingAnchor,
constant: 0
).isActive = true
toolbar.trailingAnchor.constraint(
equalTo: self.trailingAnchor,
constant: 0
).isActive = true
toolbar.topAnchor.constraint(
equalTo: self.topAnchor,
constant: 0
).isActive = true
// This is the important part:
if #available(iOS 11.0, *) {
toolbar.bottomAnchor.constraint(
equalTo: self.safeAreaLayoutGuide.bottomAnchor,
constant: 0
).isActive = true
} else {
toolbar.bottomAnchor.constraint(
equalTo: self.layoutMarginsGuide.bottomAnchor,
constant: 0
).isActive = true
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// https://stackoverflow.com/a/46510833/2603230
// 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
}
}
然后您可以將其設置為通常用於文本輸入的inputAccessoryView
:(您應該指定框架大小以避免在UIToolbar 中看到的警告與 UIBarButtonItem LayoutConstraint 問題)
let myAccessoryView = CustomInputAccessoryWithToolbarView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 44))
textView.inputAccessoryView = myAccessoryView
當你想與工具欄進行交互時(例如,在工具欄上設置項目),你可以簡單地引用toolbar
變量:
myAccessoryView.toolbar.setItems(myToolbarItems, animated: true)
演示:(在模擬器中使用硬件鍵盤/Command+K)
如果您已經通過 nib 文件加載了自定義視圖。
添加一個像這樣的便利構造函數:
convenience init() {
self.init(frame: .zero)
autoresizingMask = .flexibleHeight
}
並覆蓋intrinsicContentSize
:
override var intrinsicContentSize: CGSize {
return .zero
}
在
nib
將第一個底部約束(應保持在安全區域上方的視圖)設置為safeArea
,將第二個superview
為priority
較低的safeArea
superview
,以便在較舊的 iOS 上可以滿足。
在沒有變通方法的情況下對我有用的解決方案:
我使用UIInputViewController
通過重寫提供輸入附件視圖inputAccessoryViewController
屬性,而不是inputAccessoryView
在“主”視圖控制器。
UIInputViewController
的inputView
設置為我的自定義輸入視圖( UIInputView
子類)。
實際上對我allowsSelfSizing
是將我的UIInputView
allowsSelfSizing
屬性設置為true
。 輸入視圖內的約束使用安全區域,並以定義總視圖高度的方式設置(類似於自動調整表格視圖單元格的大小)。
-- 對於那些使用 JSQMessagesViewController 庫的人 --
我提出了一個基於 JSQ 最新develop
分支提交的固定分支。
它使用的是didMoveToWindow
解決方案(我相信來自 @jki?)。 不理想但值得嘗試,同時等待 Apple 關於inputAccessoryView
的安全區域布局指南附件或任何其他更好的修復的答案。
您可以將其添加到 Podfile 中,替換之前的 JSQ 行:
pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.