ios - iPhone X cómo manejar View Controller inputAccessoryView?




iphone-x (8)

inputAccessoryView y área segura en iPhone X

  • cuando el teclado no está visible, el inputAccessoryView se fija en la parte inferior de la pantalla. No hay forma de evitar eso y creo que este es un comportamiento intencionado.

  • Las layoutMarginsGuide (iOS 9+) y safeAreaLayoutGuide (iOS 11) de la vista configuradas como inputAccessoryView respetan el área segura, es decir, en el iPhone X:

    • cuando el teclado no está visible, la parte bottomAnchor representa el área del botón de inicio
    • cuando se muestra el teclado, la parte bottomAnchor está en la parte inferior de inputAccessoryView , de modo que no deja espacio inútil por encima del teclado

Ejemplo de trabajo:

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

Vea el resultado aquí.

Tengo una aplicación de mensajería que tiene el diseño de IU típico de un campo de texto en la parte inferior de una vista de tabla de pantalla completa. Estoy configurando ese campo de texto para que sea el inputAccessoryView del controlador de inputAccessoryView y llamo a ViewController.becomeFirstResponder() para que el campo aparezca en la parte inferior de la pantalla.

Entiendo que esta es la forma recomendada por Apple para lograr esta estructura de UI y funciona perfectamente en dispositivos "clásicos". Sin embargo, cuando pruebo en el simulador de iPhone X, observo que al utilizar este enfoque, el campo de texto no respeta las nuevas "áreas seguras". . El campo de texto se representa en la parte inferior de la pantalla debajo del indicador de la pantalla de inicio.

He mirado alrededor de los documentos HIG pero no he encontrado nada útil con respecto a inputAccessoryView en un controlador de vista.

Es difícil porque al usar este enfoque no estoy realmente en control de ninguna de las restricciones directamente, solo estoy configurando inputAccessoryView y dejando que el controlador de vista maneje la IU desde allí. Así que no puedo limitar el campo a las nuevas áreas seguras.

¿Alguien ha encontrado buena documentación sobre esto o conoce algún enfoque alternativo que funcione bien en el iPhone X?


- Para aquellos que están usando la librería JSQMessagesViewController -

Estoy proponiendo una bifurcación fija basada en el último compromiso de rama de develop JSQ.

Está utilizando la solución didMoveToWindow (de @jki, ¿creo?). No es lo ideal, pero vale la pena probar mientras espera la respuesta de Apple acerca de la guía de diseño de área segura de inputAccessoryView , o cualquier otra solución mejor.

Puede agregar esto a su Podfile, reemplazando la línea JSQ anterior:

pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true

Acabo de crear un proyecto en Github con soporte para iPhone X. Respeta la nueva guía de diseño de área segura. Utilizar:

autoresizingMask = [.flexibleHeight]

Captura de pantalla:


Acabo de crear un CocoaPod rápido llamado SafeAreaInputAccessoryViewWrapperView para solucionar este problema. También configura dinámicamente la altura de la vista ajustada utilizando restricciones de reproducción automática para que no tenga que configurar manualmente el marco. Soporta iOS 9+.

Aquí está cómo usarlo:

  1. Envuelva cualquier UIView / UIButton / UILabel / etc utilizando SafeAreaInputAccessoryViewWrapperView(for:) :

    SafeAreaInputAccessoryViewWrapperView(for: button)
  2. Almacene una referencia a esto en algún lugar de su clase:

    let button = UIButton(type: .system)
    
    lazy var wrappedButton: SafeAreaInputAccessoryViewWrapperView = {
        return SafeAreaInputAccessoryViewWrapperView(for: button)
    }()
  3. Devuelve la referencia en inputAccessoryView :

    override var inputAccessoryView: UIView? {
        return wrappedButton
    }
  4. (Opcional) Mostrar siempre el inputAccessoryView , incluso cuando el teclado está cerrado:

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

¡Buena suerte!


En Xib, encuentre una restricción correcta en la parte inferior de su diseño y configure el elemento en Safe Area lugar de Superview :

Antes de

Arreglar :

Despues


En el caso de que ya tenga una vista personalizada cargada a través de un archivo nib.

Agregue un constructor de conveniencia como este:

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

y anular intrinsicContentSize :

override var intrinsicContentSize: CGSize {
    return .zero
}

En la nib establezca la primera restricción inferior (de las vistas que deben permanecer por encima del área segura) a safeArea y la segunda a superview con una priority más baja para que pueda satisfacerse en iOS más antiguo.


Hasta que las inserciones seguras sean guiadas automáticamente por iOS, la solución simple sería envolver su accesorio en la vista de contenedor y establecer la restricción de espacio inferior entre la vista de accesorio y la vista de contenedor para que coincida con las inserciones de la ventana en el área segura.

Nota: por supuesto, esta solución puede duplicar el espacio de la vista de accesorios desde la parte inferior cuando la actualización de iOS corrige el espacio inferior para las vistas de accesorios.

P.ej

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





iphone-x