ios ios10 xcode9 iphone-x

Guías de diseño de área segura en archivos xib-iOS 10



ios10 xcode9 (9)

Comencé a adaptar mi aplicación para iPhone X y encontré un problema en Interface Builder. Se supone que las guías de diseño de área segura son compatibles con versiones anteriores, de acuerdo con los videos oficiales de Apple. Descubrí que funciona bien en los guiones gráficos.

Pero en mis archivos XIB, las guías de diseño de área segura no se respetan en iOS 10.

Funcionan bien para la nueva versión del sistema operativo, pero los dispositivos iOS 10 parecen asumir simplemente la distancia de área segura como cero (ignorando el tamaño de la barra de estado).

¿Me falta alguna configuración requerida? ¿Es un error de Xcode, y si es así, alguna solución conocida?

Aquí hay una captura de pantalla del problema en un proyecto de prueba (izquierda iOS 10, derecha iOS 11):


Actualmente, la compatibilidad con versiones anteriores no funciona bien.

Mi solución es crear 2 restricciones en el generador de interfaces y eliminar una dependiendo de la versión de ios que esté usando:

  • para view.top == safe area.top 11: view.top == safe area.top
  • para versiones anteriores: view.top == superview.top + 20

myConstraintSAFEAREA tanto como salidas como myConstraintSAFEAREA y myConstraintSUPERVIEW respectivamente. Entonces:

override func viewDidLoad() { if #available(iOS 11.0, *) { view.removeConstraint(myConstraintSUPERVIEW) } else { view.removeConstraint(myConstraintSAFEAREA) } }


Agregué una subclase NSLayoutConstraint para solucionar este problema ( IBAdjustableConstraint ), con una variable @IBInspectable , se ve así.

class IBAdjustableConstraint: NSLayoutConstraint { @IBInspectable var safeAreaAdjustedConstant: CGFloat = 0 { didSet { if OS.TenOrBelow { constant += safeAreaAdjustedConstantLegacy } } } }

Y OS.TenOrBelow

struct OS { static let TenOrBelow = UIDevice.current.systemVersion.compare("10.9", options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending }

Simplemente configúrelo como la subclase de su restricción en IB y podrá realizar <cambios específicos de iOS11. Espero que esto ayude a alguien.


Encontré la solución más simple: simplemente deshabilite safe area y use topLayoutGuide y bottomLayoutGuide + agregue soluciones para iPhone X. Quizás no sea una solución hermosa pero requiere el menor esfuerzo posible


Esto también funciona:

override func viewDidLoad() { super.viewDidLoad() if #available(iOS 11.0, *) {} else { view.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height - 80).isActive = true view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20).isActive = true } }


Hay algunos problemas con el diseño del área segura y la compatibilidad con versiones anteriores. Mira mi comentario here .

Es posible que pueda solucionar los problemas con restricciones adicionales como una prioridad 1000> = 20.0 para superview.top y una prioridad 750 == safearea.top. Si siempre muestra una barra de estado, eso debería arreglar las cosas.

Un mejor enfoque puede ser tener storyboards / xibs separados para versiones anteriores a iOS 11 y iOS-11 y posteriores, especialmente si tiene más problemas que este. La razón por la que es preferible es porque antes de iOS 11 debe aplicar restricciones a las guías de diseño superior / inferior, pero para iOS 11 debe colocarlas en áreas seguras. Las guías de diseño se han ido. Diseñar guías de diseño para versiones anteriores a iOS 11 es estilísticamente mejor que solo compensarlo con un mínimo de 20 píxeles, aunque los resultados serán el mismo IFF que siempre muestra una barra de estado.

Si adopta este enfoque, deberá configurar cada archivo en el destino de implementación correcto en el que se usará (iOS 11 o algo anterior) para que Xcode no le dé advertencias y le permita usar guías de diseño o áreas seguras, dependiendo. En su código, verifique iOS 11 en tiempo de ejecución y luego cargue el storyboard / xibs apropiado.

La desventaja de este enfoque es el mantenimiento (tendrá dos conjuntos de controladores de vista para mantener y mantener sincronizados), pero una vez que su aplicación solo sea compatible con iOS 11+ o Apple corrija la generación de restricciones de la guía de diseño de compatibilidad con versiones anteriores, puede obtener deshacerse de las versiones anteriores a iOS 11.

Por cierto, ¿cómo estás mostrando el controlador con el que estás viendo esto? ¿Es solo el controlador de vista raíz o lo presentaste, o ...? El problema que noté tiene que ver con presionar los controladores de vista, por lo que es posible que tenga un caso diferente.


He combinado algunas de las respuestas de esta página en esto, que funciona de maravilla (solo para la guía de diseño superior, como se solicita en la pregunta):

  1. Asegúrese de usar un área segura en su guión gráfico o archivo xib
  2. Restrinja sus puntos de vista a las áreas seguras
  3. Para cada view que tiene una constraint adjunta a SafeArea.top
    • Crear un IBOutlet para la view
    • Crear un IBOutler para la constraint
  4. Dentro del ViewController en viewDidLoad:

    if (@available(iOS 11.0, *)) {} else { // For each view and constraint do: [self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES; self.constraint.active = NO; }

Editar:

Aquí está la versión mejorada que terminé usando en nuestra base de código. Simplemente copie / pegue el código a continuación y conecte cada vista y restricciones a su IBOutletCollection.

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *constraintsAttachedToSafeAreaTop; @property (strong, nonatomic) IBOutletCollection(UIView) NSArray *viewsAttachedToSafeAreaTop; if (@available(iOS 11.0, *)) {} else { for (UIView *viewAttachedToSafeAreaTop in self.viewsAttachedToSafeAreaTop) { [viewAttachedToSafeAreaTop.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES; } for (NSLayoutConstraint *constraintAttachedToSafeAreaTop in self.constraintsAttachedToSafeAreaTop) { constraintAttachedToSafeAreaTop.active = NO; } }

El recuento de cada IBOutletCollection debe ser igual. por ejemplo, para cada vista debe haber su restricción asociada


Para mí, una solución simple para que funcione en ambas versiones fue

if #available(iOS 11, *) {} else { self.edgesForExtendedLayout = [] }

De la documentación: "En iOS 10 y versiones anteriores, use esta propiedad para informar qué bordes de su controlador de vista se extienden debajo de las barras de navegación u otras vistas proporcionadas por el sistema". Por lo tanto, configurarlos en una matriz vacía asegura que el controlador de vista no se extienda debajo de las barras de navegación.

Docu está disponible here


Terminé eliminando la restricción al área segura que tenía en mi archivo xib. En cambio, hice una salida al UIView en cuestión, y desde el código lo conecté así, en viewDidLayoutSubviews.

let constraint = alert.viewContents.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 0) constraint.priority = 998 constraint.isActive = true

Esto vincula una pequeña "alerta" a la parte superior de la pantalla, pero asegura que la vista de contenido dentro de la alerta esté siempre debajo del área segura superior (iOS11ish) / topLayoutGuide (iOS10ish)

Solución simple y única. Si algo se rompe, volveré 🙄.


Utilicé este, agregué el diseño del área segura superior y me conecté a la toma

@IBOutlet weak var topConstraint : NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() if !DeviceType.IS_IPHONE_X { if #available(iOS 11, *) { } else{ topConstraint.constant = 20 } } }