iPhone X comment gérer la vue contrôleur inputAccessoryView?
j'ai une application de messagerie qui a le design typique D'UI d'un champ de texte au bas d'une vue de table pleine écran. Je règle ce champ de texte pour qu'il soit le inputAccessoryView
du contrôleur de la vue et j'appelle ViewController.becomeFirstResponder()
afin d'obtenir le champ à afficher au bas de l'écran.
je comprends que C'est la façon recommandée par Apple d'accomplir cette structure D'interface utilisateur et cela fonctionne parfaitement sur les appareils "classiques" cependant quand je teste sur le simulateur iPhone X je remarque que l'utilisation de cette approche, le champ de texte ne respecte pas les nouvelles "zones de sécurité". Le champ de texte est affiché au bas de l'écran sous l'indicateur de l'écran d'accueil.
j'ai regardé autour des documents HIG mais je n'ai rien trouvé d'utile concernant le inputAccessoryView
sur un contrôleur de vue.
c'est difficile parce qu'en utilisant cette approche Je ne suis pas réellement en contrôle de l'une des contraintes directement, je ne fais que mettre le inputAccessoryView
et laisser le contrôleur de vue gérer L'interface utilisateur à partir de là. Donc je ne peux pas limiter le champ aux nouvelles zones de sécurité.
quelqu'un A trouvé une bonne documentation sur ce ou connaissez une autre approche qui fonctionne bien sur l'iPhone X?
11 réponses
inputAccessoryView
et zone de sécurité sur iPhone X
-
lorsque le clavier n'est pas visible, le
inputAccessoryView
est épinglé sur le bas de l'écran. Il n'y a pas d'autre solution et je pense que c'est un comportement intentionnel. -
les propriétés
layoutMarginsGuide
(iOS 9+) etsafeAreaLayoutGuide
(iOS 11) de la vue définie commeinputAccessoryView
respectent toutes deux l'aire de sécurité, I. e sur iPhone X:- lorsque le clavier n'est pas visible, le
bottomAnchor
représente la zone du bouton d'accueil - lorsque le clavier est affiché, le
bottomAnchor
est au bas duinputAccessoryView
, de sorte qu'il ne laisse pas d'espace inutile au-dessus du clavier
- lorsque le clavier n'est pas visible, le
exemple pratique:
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
}
}
il s'agit d'un problème général avec inputAccessoryViews sur iPhone X. La fenêtre inputAccessoryView ne tient pas compte des guides safearealayout de sa fenêtre.
pour le corriger, vous devez ajouter manuellement la contrainte dans votre classe lorsque la vue se déplace vers sa fenêtre:
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 se réfère ici à la vue inputAccessoryView.
j'ai écrit à ce sujet en détail ici: http://ahbou.org/post/165762292157/iphone-x-inputaccessoryview-fix
dans Xib, trouver une contrainte de droite à la bas de votre conception, et régler l'article à Safe Area
au lieu de Superview
:
je viens de créer un CocoaPod rapide appelé SafeAreaInputAccessoryViewWrapperview pour corriger cela. Il règle aussi dynamiquement la hauteur de la vue enveloppée en utilisant des contraintes d'autolayout pour que vous n'ayez pas à régler manuellement le cadre. Prend en charge iOS 9+.
Voici comment l'utiliser:
-
Wrap any uivi/UIButton/UILabel/etc using
SafeAreaInputAccessoryViewWrapperView(for:)
:SafeAreaInputAccessoryViewWrapperView(for: button)
-
Stocker une référence à la présente quelque part dans votre classe:
let button = UIButton(type: .system) lazy var wrappedButton: SafeAreaInputAccessoryViewWrapperView = { return SafeAreaInputAccessoryViewWrapperView(for: button) }()
-
retournez la référence dans
inputAccessoryView
:override var inputAccessoryView: UIView? { return wrappedButton }
-
(optionnel) toujours afficher le
inputAccessoryView
, même lorsque le clavier est fermé:override var canBecomeFirstResponder: Bool { return true } override func viewDidLoad() { super.viewDidLoad() becomeFirstResponder() }
bonne chance!
semble qu'il s'agisse D'un bug iOS, et il y a un problème rdar pour cela: inputAccessoryViews should respect safe area inset with external keyboard on iPhone X 151930920"
je suppose que cela devrait être corrigé dans la mise à jour iOS quand iPhone X viendra.
Jusqu'à ce que les insets sûrs soient guidés par iOS automatiquement, la solution simple serait d'envelopper votre accessoire dans la vue de conteneur et de placer la contrainte d'espace de fond entre la vue d'accesory et la vue de conteneur pour correspondre à la zone de sécurité insets de fenêtre.
Note: Bien sûr, cette solution de contournement peut doubler l'espacement des vues accessoires à partir du bas lorsque la mise à jour iOS fixe l'espacement du bas pour les vues accessoires.
E. G.
- (void) didMoveToWindow {
[super didMoveToWindow];
if (@available(iOS 11.0, *)) {
self.bottomSpaceConstraint.constant = self.window.safeAreaInsets.bottom;
}
}
il suffit d'ajouter une extension pour 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
}
}
}
}
"151910920 double": jsqmessageviewcontroller ios11 de la barre d'outils
-- pour ceux qui utilisent le JSQMessagesViewController lib --
je propose une fourchette fixe basée sur le dernier commit de branche develop
du JSQ.
il utilise la solution didMoveToWindow
(de @jki je crois?). Pas idéal, mais vaut la peine d'essayer en attendant la réponse D'Apple à propos de inputAccessoryView
de l 'attachement du guide de mise en page zone de sécurité, ou toute autre meilleure solution.
vous pouvez ajouter ceci à votre Podfile, en remplaçant le ligne JSQ précédente:
pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true
du Code Swift 4. Idée - surveillance layoutMarginsDidChange
et le réglage 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])
}
}
Utilisation:
let autoSuggestionView = AutoSuggestionView()
// ...
textField.inputAccessoryView = autoSuggestionView
résultat:
dans le cas où vous avez déjà une vue personnalisée chargée via le fichier nib.
Ajouter un constructeur de commodité comme ceci:
convenience init() {
self.init(frame: .zero)
autoresizingMask = .flexibleHeight
}
et remplacer intrinsicContentSize
:
override var intrinsicContentSize: CGSize {
return .zero
}
dans le
nib
placer la première contrainte de fond (des vues qui devraient rester au-dessus de la zone de sécurité) àsafeArea
et la deuxième àsuperview
avec plus baspriority
de sorte qu'il peut être satisfait sur iOS plus anciens.
je suis juste ajouter zone de sécurité à inputAccessoryView (case à cocher à Xcode). Et changer la contrainte d'espace de fond égale au fond de la zone de sécurité au lieu de inputAccessoryView vue de la racine de fond.