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):
- Asegúrese de usar un área segura en su guión gráfico o archivo xib
- Restrinja sus puntos de vista a las áreas seguras
-
Para cada
view
que tiene unaconstraint
adjunta a SafeArea.top-
Crear un IBOutlet para la
view
-
Crear un IBOutler para la
constraint
-
Crear un IBOutlet para la
-
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
}
}
}