wake-up-neo.com

iPhone X wie mit View Controller inputAccessoryView umgehen?

Ich habe eine Messaging-App, die das typische UI-Design eines Textfelds am unteren Rand einer Vollbild-Tabellenansicht hat. Ich setze dieses Textfeld als inputAccessoryView des View-Controllers ein und rufe ViewController.becomeFirstResponder() auf, damit das Feld am unteren Rand des Bildschirms angezeigt wird.

Ich verstehe, dass dies der von Apple empfohlene Weg ist, diese UI-Struktur zu erreichen, und es funktioniert perfekt auf "klassischen" Geräten. Beim Testen des iPhone X-Simulators stelle ich jedoch fest, dass das Textfeld die neuen "sicheren Bereiche" nicht beachtet. . Das Textfeld wird ganz unten auf dem Bildschirm unter der Startanzeige angezeigt.

Ich habe mich in den HIG-Dokumenten umgesehen, aber nichts Nützliches bezüglich inputAccessoryView auf einem View-Controller gefunden. 

Es ist schwierig, da ich mit dieser Methode eigentlich keine der Einschränkungen direkt kontrolliere. Ich setze lediglich inputAccessoryView und lasse den View-Controller die Benutzeroberfläche von dort aus bedienen. Ich kann das Feld also nicht einfach auf die neuen sicheren Bereiche beschränken.

Hat jemand eine gute Dokumentation dazu gefunden oder einen alternativen Ansatz kennen, der auf dem iPhone X gut funktioniert?

 enter image description here

16
LOP_Luke

inputAccessoryView und sicherer Bereich auf dem iPhone X

  • wenn die Tastatur nicht sichtbar ist, wird die inputAccessoryView ganz unten auf dem Bildschirm fixiert. Daran führt kein Weg vorbei und ich denke, das ist beabsichtigtes Verhalten.

  • die Eigenschaften layoutMarginsGuide (iOS 9+) und safeAreaLayoutGuide (iOS 11) der Ansicht, die als inputAccessoryView festgelegt ist, berücksichtigen beide den sicheren Bereich, d. h. auf dem iPhone X:

    • wenn die Tastatur nicht sichtbar ist, berücksichtigt die Variable bottomAnchor den Bereich der Startschaltfläche
    • wenn die Tastatur angezeigt wird, befindet sich die bottomAnchor am unteren Rand der inputAccessoryView, so dass kein unbrauchbarer Platz über der Tastatur verbleibt

Arbeitsbeispiel:

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
    }
}

Sehen Sie das Ergebnis hier

28
Alexandre Bintz

Dies ist ein allgemeines Problem mit inputAccessoryViews auf iPhone X .. __ Die inputAccessoryView ignoriert die safeAreaLayoutGuides ihres Fensters.

Um das Problem zu beheben, müssen Sie die Einschränkung in Ihrer Klasse manuell hinzufügen, wenn die Ansicht in ihr Fenster verschoben wird:

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 bezieht sich hier auf inputAccessoryView.

Ich habe hier ausführlich darüber geschrieben: http://ahbou.org/post/165762292157/iphone-x-inputaccessoryview-fix

25
ahbou

Suchen Sie in Xib eine rechte Einschränkung am bottom Ihres Designs und setzen Sie das Element auf Safe Area anstelle von Superview:

Vor:  enter image description here

Fix:  enter image description here

Nach dem:  enter image description here

4
Vlad

Ich habe gerade einen schnellen CocoaPod mit dem Namen SafeAreaInputAccessoryViewWrapperView erstellt, um dies zu beheben. Außerdem wird die Höhe der umschlossenen Ansicht mithilfe von Autolayout-Einschränkungen dynamisch festgelegt, sodass Sie den Rahmen nicht manuell festlegen müssen. Unterstützt iOS 9+.

So verwenden Sie es:

  1. Umhüllen Sie UIView/UIButton/UILabel/etc mit SafeAreaInputAccessoryViewWrapperView(for:):

    SafeAreaInputAccessoryViewWrapperView(for: button)
    
  2. Speichern Sie einen Verweis darauf irgendwo in Ihrer Klasse:

    let button = UIButton(type: .system)
    
    lazy var wrappedButton: SafeAreaInputAccessoryViewWrapperView = {
        return SafeAreaInputAccessoryViewWrapperView(for: button)
    }()
    
  3. Gib die Referenz in inputAccessoryView zurück:

    override var inputAccessoryView: UIView? {
        return wrappedButton
    }
    
  4. (Optional) Zeigen Sie immer inputAccessoryView an, auch wenn die Tastatur geschlossen ist:

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

Viel Glück!

3
Jeff

Fügen Sie einfach eine Erweiterung für JSQMessagesInputToolbar hinzu

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
            }
        }
     }
}

duplizieren: jsqmessageviewcontroller ios11 toolbar

3
ERbittuu

Anscheinend handelt es sich um einen iOS-Fehler, und es gibt ein Problem: inputAccessoryViews sollte den sicheren Bereich mit externer Tastatur auf dem iPhone X berücksichtigen

Ich denke, das sollte im iOS Update behoben werden, wenn iPhone X erscheint.

2
Marat Saytakov

Solange die Insets nicht automatisch von iOS geführt werden, besteht eine einfache Problemumgehung darin, das Zubehör in der Containeransicht einzuwickeln und die Bodenbeschränkung zwischen der Accesory-Ansicht und der Containeransicht festzulegen, um die Insets des sicheren Bereichs des Fensters anzupassen. 

Hinweis: Natürlich kann diese Problemumgehung den Abstand der Zubehöransicht vom unteren Rand verdoppeln, wenn das iOS-Update den unteren Abstand für die Zubehörsicht festlegt.

Z.B.

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

Vom Code (Swift 4). Idee - Überwachung layoutMarginsDidChange Ereignis und Anpassung 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])
   }
}

Verwendungszweck:

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

Ergebnis:

 enter image description here  enter image description here

1
Vlad

Für den Fall, dass Sie bereits eine benutzerdefinierte Ansicht per NIB-Datei geladen haben.

Füge einen Convenience-Konstruktor hinzu:

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

und überschreiben intrinsicContentSize:

override var intrinsicContentSize: CGSize {
    return .zero
}

In nib setzen Sie die erste untere Einschränkung (von Ansichten, die über dem sicheren Bereich bleiben sollen) auf safeArea und die zweite auf superview mit niedrigerer priority, damit sie unter älteren iOS-Versionen erfüllt werden kann.

0
Jakub Truhlář