I have a messaging app that has the typical UI design of a text field at the bottom of a full screen table view. I am setting that text field to be the view controller\'s
Solution that worked for me without workarounds:
I'm using UIInputViewController
for providing input accessory view by overriding inputAccessoryViewController
property instead of inputAccessoryView
in the "main" view controller.
UIInputViewController
's inputView
is set to my custom input view (subclass of UIInputView
).
What actually did the trick for me is setting allowsSelfSizing
property of my UIInputView
to true
. The constraints inside input view use safe area and are set up in a way that defines total view's height (similar to autoresizing table view cells).
Just add one extension for 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 toolbar
From code (Swift 4). Idea - monitoring layoutMarginsDidChange
event and adjusting 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])
}
}
Usage:
let autoSuggestionView = AutoSuggestionView()
// ...
textField.inputAccessoryView = autoSuggestionView
Result:
-- For those who are using the JSQMessagesViewController lib --
I am proposing a fixed fork based on the JSQ latest develop
branch commit.
It is using the didMoveToWindow
solution (from @jki I believe?). Not ideal but worth to try while waiting for Apple's answer about inputAccessoryView
's safe area layout guide attachment, or any other better fix.
You can add this to your Podfile, replacing the previous JSQ line:
pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true
In Xib, find a right constraint at the bottom of your design, and set item to Safe Area
instead of Superview
:
Before:
Fix:
After:
Seems it's an iOS bug, and there is a rdar issue for it: inputAccessoryViews should respect safe area inset with external keyboard on iPhone X
I guess this should be fixed in iOS update when iPhone X will come up.