una - iOS 11 guía de diseño de área segura compatibilidad con versiones anteriores
los audifonos del iphone 7 son compatibles con el iphone 5s (13)
Cuando tiene un ViewController genérico que todos sus ViewControllers extienden, otra solución sería colocar los elementos que deberían ajustarse en una IBOutletCollection y ajustarlos mediante programación en ese GenericViewController. Aquí está mi código:
@IBOutlet var adjustTopSpaceViews: [UIView]?
override func viewDidLoad() {
super.viewDidLoad()
adjustViews()
....
}
func adjustViews() {
guard let views = adjustTopSpaceViews,
ProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11 else {
return
}
let statusBarHeight = UIApplication.shared.statusBarFrame.height
for subview in views {
subview.superview?.constraints.filter({ (constraint) -> Bool in
return constraint.firstAttribute == .top
&& constraint.secondAttribute == .top
&& (constraint.firstItem as? UIView == subview || constraint.secondItem as? UIView == subview)
}).forEach({ (constraint) in
constraint.constant += (constraint.firstItem as? UIView == subview) ? statusBarHeight : -statusBarHeight
})
}
}
Definitivamente hay al menos un problema de compatibilidad con las restricciones de área segura de iOS 11 que he observado en el Xcode 9 GM: controladores de vista con restricciones de área segura.
Si su barra de navegación está oculta y empuja una vista restringida por un área segura, la vista empujada se superpondrá a la barra de estado en iOS 9 y 10.
Si la barra de navegación está visible y "debajo de las barras superiores" está deshabilitada, la vista empujada seguirá deslizándose hacia arriba debajo de la barra de navegación para llegar a la parte superior de la pantalla. La barra de navegación está colocada correctamente.
En iOS 11, el diseño será correcto en ambos casos.
Aquí hay un ejemplo simple: http://www.filedropper.com/foobar
Y aquí hay un video con la barra de navegación oculta (iOS 10.3 a la izquierda, iOS 11 a la derecha): https://vimeo.com/234174841/1e27a96a87
Aquí hay una versión donde la barra de navegación está visible (habilitada en la punta): https://vimeo.com/234316256/f022132d57
Archivé esto como Radar # 34477706.
Gracias a @Sander por señalar el caso visible de la barra de navegación.
En Objective-C para el margen superior e inferior cuando está en iPhone-X
if (@available(iOS 11, *)) {
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.parentView.safeAreaLayoutGuide
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.parentView.safeAreaLayoutGuide
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
} else {
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.parentView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.childView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.parentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
}
Encontré una forma más conveniente donde solo necesitas subclasificar la
NSLayoutConstraint
que está
safeArea
a tu
safeArea
.
Es un poco extraño ya que tienes que obtener el ViewController desde una UIView pero, en mi opinión, es una alternativa fácil y buena hasta que Apple finalmente arregle la compatibilidad con versiones anteriores de safeArea en Xibs.
Subclase:
class SafeAreaBackwardsCompatabilityConstraint: NSLayoutConstraint {
private weak var newConstraint: NSLayoutConstraint?
override var secondItem: AnyObject? {
get {
if #available(iOS 11.0, *) {}
else {
if let vc = (super.secondItem as? UIView)?.parentViewController, newConstraint == nil {
newConstraint = (self.firstItem as? UIView)?.topAnchor.constraint(equalTo: vc.topLayoutGuide.bottomAnchor)
newConstraint?.isActive = true
newConstraint?.constant = self.constant
}
}
return super.secondItem
}
}
override var priority: UILayoutPriority {
get {
if #available(iOS 11.0, *) { return super.priority }
else { return 750 }
}
set { super.priority = newValue }
}
}
private extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Xib:
Estaba usando esto en Objective-C con buenos resultados para iOS 10. En caso de que use SafeArea en su xib, entonces puede agregar en su
viewDidLoad
:
if (@available(iOS 11.0, *)) {}
else {
self.edgesForExtendedLayout = UIRectEdgeNone;
}
Esto es lo que hice con mis proyectos
En mi caso, tanto mi
topConstraint
como
bottomConstraint
son
@IBOutlet
s.
Esto también es compatible con
iOS 8
.
Mi configuración inicial para las restricciones superior e inferior son para los iPhones normales, por eso solo estoy editando las restricciones para el iPhone X
// iOS 11 Layout Fix. (For iPhone X)
if #available(iOS 11, *) {
self.topConstraint.constant = self.topConstraint.constant + self.view.safeAreaInsets.top
self.bottomConstraint.constant = self.bottomConstraint.constant + self.view.safeAreaInsets.bottom
}
.
NOTA:
self.view
es su superView, por eso lo estoy usando para
safeAreaInsets
La "guía de diseño de área segura" es compatible con versiones anteriores. Bueno, a menos que lo uses en xib. Con el guión gráfico parece estar bien.
Resolví mi problema accediendo a la "restricción de diseño superior" desde el primer objeto en la parte superior de mi vista.
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint;
luego, cambié el valor Constante a esa restricción y actualicé la vista. Por ejemplo, si usa una barra de navegación (altura 44) más la barra de estado (altura 20):
if (SYSTEM_VERSION_LESS_THAN(@"11.0")) {
_topLayoutConstraint.constant = 64;
[self.view layoutIfNeeded];
}
Con SYSTEM_VERSION_LESS_THAN que se define así:
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
La compatibilidad con versiones anteriores de Safe Areas para iOS 9 y iOS 10 solo funciona si está utilizando guiones gráficos. Si está utilizando xibs, no hay una guía de diseño a la que recurrir. https://forums.developer.apple.com/thread/87329
Las soluciones alternativas parecen ser
(a) migre sus xibs a guiones gráficos, o
(b) agregue algunas restricciones adicionales mediante programación.
Si (a) no es realmente una opción, el enfoque manual será algo como esto:
Suponiendo que tiene una vista en su xib que desea mantener dentro del área segura (es decir, debajo de cualquier barra de estado o barra de navegación).
-
Agregue restricciones en su xib entre su vista y el área segura para iOS 11. Asigne la restricción superior a una prioridad de 750.
-
En su controlador de vista, agregue una propiedad:
@property (nonatomic, strong) NSLayoutConstraint *topLayoutConstraint;
Y luego en viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; if (@available(iOS 11, *)) { // safe area constraints already set } else { if (!self.topLayoutConstraint) { self.topLayoutConstraint = [self.<yourview>.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor]; [self.topLayoutConstraint setActive:YES]; } } }
La nueva restricción solo se creará para iOS 9 y iOS 10, tiene una prioridad predeterminada de 1000 y anula la de xib.
-
Repita para una restricción inferior si necesita evitar el indicador de inicio.
Versión Swift 4:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if #available(iOS 11, *) {
// safe area constraints already set
} else {
if topLayoutConstraint == nil {
topLayoutConstraint = <yourview>.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
topLayoutConstraint?.isActive = true
}
}
}
Logré trabajar con las nuevas guías de diseño de Área segura y mantener la compatibilidad con iOS 9 e iOS 10: (EDITAR: como se señala en los comentarios de @NickEntin, esta implementación supondrá que hay una barra de estado presente, que no lo hará sea cierto en el paisaje en el iPhone X. Dando lugar a mucho espacio en la parte superior (20 puntos). Sin embargo, funcionará perfectamente bien.
Por ejemplo, si desea que una vista esté 10 puntos debajo de la barra de estado (y 10 puntos debajo de la carcasa del sensor en el iPhone X):
-
En su XIB, vaya al
File Inspector
y active la caja fuerte marcandoUse Safe Area Layout Guides
. -
Cree una restricción desde la parte superior de la vista hasta la parte superior de la vista principal, con la restricción
>=
(mayor o igual), constante30
(30 porque queremos un espacio de 10 puntos en la barra de estado que tiene 20 puntos de alto) y prioridadHigh
(750) . -
Cree una restricción desde la parte superior de la vista hasta la parte superior del Área segura, con
=
(igual) restricción, constante10
y prioridadLow
(250).
Lo mismo se puede hacer para una vista en la parte inferior (y para el inicio / final o izquierda / derecha al Área segura):
-
En su XIB, vaya al
File Inspector
y active la caja fuerte marcandoUse Safe Area Layout Guides
. -
Cree una restricción desde la parte inferior de la vista hasta la parte inferior de la vista principal, con una restricción
>=
(mayor o igual), constante10
y prioridadHigh
(750). -
Cree una restricción desde la parte inferior de la vista hasta la parte inferior del Área segura, con
=
(igual) restricción, constante10
y prioridadLow
(250).
Sí, su proyecto / aplicación funcionará en versiones de iOS anteriores a iOS 11 sin ningún problema. En las versiones de iOS anteriores a la 11, reemplaza / considera el diseño de área segura en un diseño automático normal y sigue las reglas de la guía de diseño superior e inferior.
Probé mi proyecto existente con y sin ''SafeAreaLayout'' en ambas plataformas (iOS 11 y iOS 10 anterior). Funciona bien
Solo asegúrate:
Si ha diseñado su proyecto / interfaz de usuario en AutoLayout; Las restricciones de su UIElement siguen / relativas a la guía de diseño Superior e Inferior (no a la supervista). Entonces, con un solo clic (habilitar) en la opción SafeAreaLayout, implementará automáticamente el diseño SafeArea correctamente para todos los archivos de Interface Builder en su guión gráfico.
Si ha diseñado su proyecto / interfaz de usuario en SafeAreaLayout; luego seguirá automáticamente la guía de diseño Superior e Inferior en iOS anterior.
Aquí hay una instantánea de muestra con resultado. Al habilitar o deshabilitar el diseño del Área segura, no afectará el diseño existente.
Diseño automático
En resumen, una respuesta a su pregunta es: "Habilitar guías de diseño de área segura compatibles con iOS antes de las 11"
Puede implementar el diseño de área segura en su proyecto / aplicación y funcionará bien con versiones anteriores de iOS al convertir el diseño de área segura en diseño superior e inferior.
Si usa xibs sin storyboard, entonces no tienen guías de diseño en ios 10. Por lo tanto, mueva xib al storyboard para tener compatibilidad con versiones anteriores.
Tengo problemas de compatibilidad con WKWebView & Safe Area en iOS 9. Por alguna razón, WKWebView simplemente ignora la configuración del diseño del área segura.
Aquí está mi contenedor de soluciones iOS 9 a iOS 11+ en swift 4+
let safeAreaTopAnchor:NSLayoutYAxisAnchor?
if #available(iOS 11.0, *) {
safeAreaTopAnchor = contentView.safeAreaLayoutGuide.topAnchor
} else {
// Fallback on earlier versions
var parentViewController: UIViewController? {
var parentVCResponder: UIResponder? = self
while parentVCResponder != nil {
parentVCResponder = parentVCResponder!.next
if let viewController = parentVCResponder as? UIViewController {
return viewController
}
}
return nil
}
safeAreaTopAnchor = parentViewController?.topLayoutGuide.bottomAnchor
}