web-dev-qa-db-fra.com

Guide d'implantation des zones de sécurité iOS 11, compatibilité avec les versions antérieures

L'activation des guides de mise en forme de zones sûres est-elle compatible avec iOS au-dessous de 11?

enter image description here

59
Ted

J'ai réussi à travailler avec les nouveaux guides d'agencement de la zone sécurisée et à maintenir une compatibilité ascendante avec iOS 9 et iOS 10: (EDIT: comme indiqué dans les commentaires de @NickEntin, cette implémentation présumera qu'il existe une barre d'état présente, ce qui ne sera pas vrai en mode paysage sur l'iPhone X. Il en résultera trop d'espace au sommet (20 points). Cela fonctionnera toutefois parfaitement.

Par exemple. si vous souhaitez qu'une vue soit 10 points sous la barre d'état (et 10 points sous le boîtier du capteur sur l'iPhone X):

  1. Dans votre XIB, accédez à File Inspector et activez le coffre-fort en vérifiant Use Safe Area Layout Guides.
  2. Créez une contrainte du haut de la vue vers le haut de la vue principale, avec >= contrainte (supérieure ou égale à), constante 30 (30 car nous voulons un espacement de 10 points de la barre d'état qui est haut de 20 points ) et priorité High (750).
  3. Créez une contrainte du haut de la vue vers le haut de la zone sécurisée, avec la contrainte = (égale), la constante 10 et la priorité Low (250).

La même chose peut être faite pour une vue en bas (et pour le début/fin ou la gauche/droite vers la zone de sécurité):

  1. Dans votre XIB, accédez à File Inspector et activez le coffre-fort en vérifiant Use Safe Area Layout Guides.
  2. Créez une contrainte du bas de la vue vers le bas de la vue principale, avec la contrainte >= (supérieure ou égale), la constante 10 et la priorité High (750).
  3. Créez une contrainte du bas de la vue vers le bas de la zone sécurisée, avec la contrainte = (égale), la constante 10 et la priorité Low (250).
53
thijsonline

La compatibilité ascendante de Zones sécurisées pour iOS 9 et iOS 10 ne fonctionne que si vous utilisez des storyboards. Si vous utilisez xibs, il n’ya pas de guide de mise en page auquel vous pouvez vous référer. https://forums.developer.Apple.com/thread/87329

Les solutions de contournement semblent être soit

(a) migrez vos xibs dans des storyboards, ou

(b) ajouter des contraintes supplémentaires par programme.

Si (a) n'est pas vraiment une option, l'approche manuelle ressemblera à ceci:

En supposant que vous ayez dans votre xib une vue que vous souhaitez conserver dans la zone de sécurité (c'est-à-dire en dessous de la barre d'état ou de la barre de navigation).

  1. Ajoutez des contraintes dans votre xib entre votre vue et la zone sécurisée pour iOS 11. Attribuez à la contrainte supérieure une priorité de 750.

    add a top constraint

  2. Dans votre contrôleur de vue, ajoutez une propriété:

    @property (nonatomic, strong) NSLayoutConstraint *topLayoutConstraint;
    

    Et ensuite dans 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 nouvelle contrainte ne sera créée que pour iOS 9 et iOS 10, a une priorité par défaut de 1000 et remplace celle de xib.

  3. Répétez l'opération pour une contrainte de fond si vous devez éviter l'indicateur de maison.

Version de 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
        }
    }
}
45
Dobster

Il existe au moins un problème de compatibilité ascendante avec les contraintes de zone de sécurité d'iOS 11 que j'ai observées dans Xcode 9 GM: les contrôleurs de vue poussés avec des contraintes de zone de sécurité.

Si votre barre de navigation est masquée et que vous affichez une vue limitée en haut d'une zone sécurisée, la vue poussée chevauchera la barre d'état sur iOS 9 & 10.

Si la barre de navigation est visible et que l'option "sous les barres du haut" est désactivée, la vue poussée continuera à glisser vers le haut sous la barre de navigation pour atteindre le haut de l'écran. La barre de navigation est correctement placée.

Sur iOS 11, la mise en page sera correcte dans les deux cas.

Voici un exemple simple: http://www.filedropper.com/foobar

Et voici une vidéo de cette barre de navigation masquée (iOS 10.3 à gauche, iOS 11 à droite): https://vimeo.com/234174841/1e27a96a87

Voici une version où la barre de navigation est visible (activée dans la nib): https://vimeo.com/234316256/f022132d57

J'ai déposé cela comme radar # 34477706.

Merci à @ Sander pour avoir signalé le cas visible de la barre de navigation.

18
clarus

Si vous utilisez xibs sans storyboard, ils ne disposent pas de guides de mise en page sur ios 10. Par conséquent, déplacez xib vers le storyboard pour obtenir une compatibilité ascendante.

7
ihar

Oui, votre projet/application fonctionnera dans les versions iOS antérieures à iOS 11 sans aucun problème. Dans les versions iOS antérieures à la version 11, il remplace/considère la mise en forme de la zone sécurisée dans la mise en forme automatique normale et respecte le guide de présentation des règles de haut et de bas.

J'ai testé mon projet existant avec et sans 'SafeAreaLayout' sur les deux plates-formes (iOS 11 et iOS 10 arrière). Ça fonctionne bien.

Assurez-vous juste:

  • Si vous avez conçu votre projet/interface utilisateur dans AutoLayout; les contraintes de votre UIElement suivent/par rapport au guide de disposition Haut et Bas (ne pas afficher de vue d'ensemble). Ainsi, par un simple clic (activation) sur l'option SafeAreaLayout, la mise en page SafeArea sera automatiquement mise en œuvre correctement pour tous les fichiers Interface Builders de votre storyboard.

  • Si vous avez conçu votre projet/interface utilisateur dans SafeAreaLayout; alors il suivra automatiquement le guide de disposition supérieur et inférieur dans iOS arrière.

Voici un exemple d'instantané avec résultat. Activer ou désactiver la disposition de la zone sécurisée n'a aucune incidence sur la conception existante.

Disposition de la zone sécurisée: enter image description here

AutoLayout

enter image description here

En bref, la réponse à votre question est la suivante: "Activer les guides de présentation de la zone sécurisée compatibles avec iOS avant 11"

Vous pouvez implémenter la disposition de la zone sécurisée dans votre projet/application et cela fonctionnera correctement avec les versions précédentes d’IOS en convertissant la disposition de la zone sécurisée en dispositions de haut et de bas.

7
Krunal

J'utilisais cela dans Objective-C avec un bon résultat pour iOS 10. Si vous utilisez SafeArea dans votre xib, vous pouvez ajouter dans votre viewDidLoad:

if (@available(iOS 11.0, *)) {}
else {
    self.edgesForExtendedLayout = UIRectEdgeNone;
}
4
Dmitry A.

J'ai trouvé un moyen plus pratique où il suffit de sous-classer la NSLayoutConstraintqui est épinglée à votre safeArea.

C'est un peu hacky puisque vous devez obtenir le ViewController d'un UIView, mais à mon avis, c'est une alternative simple et efficace jusqu'à ce que Apple corrige enfin la compatibilité avec les versions antérieures de safeArea dans Xibs.

Sous-classe:

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:

enter image description here

3
iVentis

Le "guide de disposition des zones sûres" est compatible avec les versions antérieures. Eh bien, sauf si vous l'utilisez dans xib. Avec le storyboard, ça semble bien.

J'ai résolu mon problème en accédant à la "contrainte de disposition maximale" du premier objet situé en haut de ma vue.

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint;

ensuite, j'ai changé la valeur de Constant pour cette contrainte et actualiser la vue. Par exemple, si vous utilisez une barre de navigation (hauteur 44) plus la barre d'état (hauteur 20):

if (SYSTEM_VERSION_LESS_THAN(@"11.0")) {
    _topLayoutConstraint.constant = 64;
    [self.view layoutIfNeeded];
}

Avec SYSTEM_VERSION_LESS_THAN qui est défini comme ça:

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
3
gduh

J'ai des problèmes de compatibilité ascendante avec WKWebView et la zone sécurisée sous iOS 9. Pour une raison quelconque, WKWebView ignore simplement les paramètres de disposition de la zone sécurisée.

2
Nik

En Objective-C pour les marges supérieure et inférieure sur 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];

}
2
Chandan

Lorsque vous avez un ViewController générique que tous vos ViewControllers étendent, une autre solution consisterait à placer les éléments devant être ajustés dans un IBOutletCollection et à les ajuster par programme dans ce GenericViewController. Voici mon code:

@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
        })
    }
}
2
Youssef MAHERZI

Voici ce que j'ai fait avec mes projets

Dans mon cas, mes topConstraint et bottomConstraints sont @IBOutlets. Ceci est également compatible pour iOS 8.

Ma configuration initiale pour les contraintes du haut et du bas concerne les iPhones normaux. C’est pourquoi je n’édite que les contraintes pour 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
    }

.

NOTE:self.view est votre superView, c'est pourquoi je l'utilise pour safeAreaInsets

2
Zonily Jame

Voici mon wrapper de solution iOS 9 à iOS 11+ dans 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

    }
1