Comment utiliser la disposition de la zone de sécurité par programmation?

99

Comme je n'utilise pas de storyboards pour créer mes vues, je me demandais s'il y avait l'option "Utiliser les guides de zone sûre" par programmation ou quelque chose comme ça.

J'ai essayé d'ancrer mes points de vue sur

view.safeAreaLayoutGuide

mais ils continuent de chevaucher le sommet du simulateur iPhone X.

Phillip
la source
1
Aucune propriété booléenne documentée à ce sujet selon: developer.apple.com/documentation/uikit/uiview/…
dvp.petrov
Et pourquoi pas view.safeAreaInsets? Avez-vous essayé cela?
Karthikeyan Bose
@KarthikeyanBose oui je l'ai fait sans chance malheureusement.
Phillip
travaille pour moi. À quoi ressemble le code
Daniel Springer

Réponses:

155

Voici un exemple de code (Réf. De: Safe Area Layout Guide ):
Si vous créez vos contraintes dans le code, utilisez la propriété safeAreaLayoutGuide de UIView pour obtenir les ancres de disposition appropriées. Recréons l'exemple d'Interface Builder ci-dessus dans le code pour voir à quoi il ressemble:

En supposant que nous ayons la vue verte en tant que propriété dans notre contrôleur de vue:

private let greenView = UIView()

Nous pourrions avoir une fonction pour configurer les vues et les contraintes appelées depuis viewDidLoad:

private func setupView() {
  greenView.translatesAutoresizingMaskIntoConstraints = false
  greenView.backgroundColor = .green
  view.addSubview(greenView)
}

Créez les contraintes de marge de début et de fin comme toujours à l'aide du layoutMarginsGuide de la vue racine:

 let margins = view.layoutMarginsGuide
 NSLayoutConstraint.activate([
    greenView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
    greenView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
 ])

Désormais, à moins que vous ne cibliez iOS 11 et versions ultérieures, vous devrez encapsuler les contraintes du guide de mise en page de zone de sécurité avec #available et revenir aux guides de mise en page supérieur et inférieur pour les versions antérieures d'iOS:

if #available(iOS 11, *) {
  let guide = view.safeAreaLayoutGuide
  NSLayoutConstraint.activate([
   greenView.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0),
   guide.bottomAnchor.constraintEqualToSystemSpacingBelow(greenView.bottomAnchor, multiplier: 1.0)
   ])
} else {
   let standardSpacing: CGFloat = 8.0
   NSLayoutConstraint.activate([
   greenView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: standardSpacing),
   bottomLayoutGuide.topAnchor.constraint(equalTo: greenView.bottomAnchor, constant: standardSpacing)
   ])
}

Résultat:

entrez la description de l'image ici

entrez la description de l'image ici


Voici la documentation officielle des développeurs Apple pour le guide de disposition des zones de sécurité


La zone de sécurité est requise pour gérer la conception de l'interface utilisateur pour iPhone-X. Voici les instructions de base pour concevoir une interface utilisateur pour iPhone-X à l'aide de la disposition de la zone de sécurité

Krunal
la source
2
Serait-il possible de donner cela également dans l'objectif-C? Cela ressemble à ce dont j'ai besoin
Tom Hammond
6
@TomHammond Voici en Objective-C pour vous stackoverflow.com/a/47076040/5638630
Krunal
2
@ZonilyJame Maintenant à propos de votre requête - SaFeAreaLayout est un cadre spécifique à iOS (pas spécifique à un appareil iPhoneX), il remplace le guide de mise en page haut-bas dans iOS 11, nous devons donc utiliser / définir la condition pour iOS et non pour l'appareil. SafeAreaLayout s'occupe de la conception de tous les types d'appareils (iPhone-X et autres). Vous pouvez me demander plus de détails, si vous avez encore des questions / confusion.
Krunal
3
Impressionnant. Merci :)
Rajamohan S
1
pourquoi est-il accepté comme bonne réponse? J'ai essayé de configurer une position concrète - le résultat est entièrement aléatoire - le contrôle est placé à une position imprévisible!
Vyachaslav Gerchicov
82

J'utilise en fait une extension et je contrôle s'il s'agit d'ios 11 ou non.

extension UIView {

  var safeTopAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.topAnchor
    }
    return self.topAnchor
  }

  var safeLeftAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *){
      return self.safeAreaLayoutGuide.leftAnchor
    }
    return self.leftAnchor
  }

  var safeRightAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *){
      return self.safeAreaLayoutGuide.rightAnchor
    }
    return self.rightAnchor
  }

  var safeBottomAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.bottomAnchor
    }
    return self.bottomAnchor
  }
}
Alper
la source
C'est une façon si simple d'aller et fait l'affaire. Merci pour l'idée.
alper_k
C'est une façon si simple mais agréable de le faire! Notez l'utilisation de self.safeAreaLayoutGuideau lieu de self.layoutMarginsGuide. Le coffre-fort utilisé dans cette réponse a fonctionné correctement pour que je reste dans la zone de sécurité! Une chose que je suggérerais de changer serait d'utiliser leadingAnchoret trailingAnchorau lieu de leftAnchoret rightAnchor. Bravo!
Pranoy C
22

SafeAreaLayoutGuideest la UIViewpropriété,

Le haut du safeAreaLayoutGuide indique le bord supérieur non masqué de la vue (par exemple, pas derrière la barre d'état ou la barre de navigation, le cas échéant). De même pour les autres bords.

À utiliser safeAreaLayoutGuidepour éviter que nos objets ne se coupent / se chevauchent à partir des coins arrondis, des barres de navigation, des barres d'onglets, des barres d'outils et d'autres vues d'ancêtres.

Nous pouvons créer respectivement des safeAreaLayoutGuideobjets et des contraintes d'objet.

Contraintes pour Portrait + Paysage est -

Image portrait

Image de paysage

        self.edgesForExtendedLayout = []//Optional our as per your view ladder

        let newView = UIView()
        newView.backgroundColor = .red
        self.view.addSubview(newView)
        newView.translatesAutoresizingMaskIntoConstraints = false
        if #available(iOS 11.0, *) {
            let guide = self.view.safeAreaLayoutGuide
            newView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
            newView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
            newView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
            newView.heightAnchor.constraint(equalToConstant: 100).isActive = true

        }
        else {
            NSLayoutConstraint(item: newView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: newView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: newView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true

            newView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        }

UILayoutGuide

safeAreaLayoutGuide

Jack
la source
2
Ne faites jamais de contraintes de configuration dans le viewDidAppear, sauf si vous savez absolument ce que vous faites. viewDidAppearest appelée plusieurs fois et donc, vos contraintes seront dupliquées à chaque fois qu'elle sera appelée.
Yevhen Dubinin
Oui! , Réponse modifiée, vous pouvez l'utiliser comme cas d'utilisation.
Jack
14

Pour ceux d'entre vous qui utilisent SnapKit , tout comme moi, la solution est d'ancrer vos contraintes pour view.safeAreaLayoutGuideaimer ainsi:

yourView.snp.makeConstraints { (make) in
    if #available(iOS 11.0, *) {
        //Bottom guide
        make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottomMargin)
        //Top guide
        make.top.equalTo(view.safeAreaLayoutGuide.snp.topMargin)
        //Leading guide
        make.leading.equalTo(view.safeAreaLayoutGuide.snp.leadingMargin)
        //Trailing guide
        make.trailing.equalTo(view.safeAreaLayoutGuide.snp.trailingMargin)

     } else {
        make.edges.equalToSuperview()
     }
}
Phillip
la source
1
Très bonne réponse. Pour le rendre un peu plus compact, vous pouvez dire: if #available (iOS 11.0, *) {make.edges.equalTo (view.safeAreaLayoutGuide.snp.margins)}
Don Miguel
7

J'utilise ceci au lieu d'ajouter des contraintes de marge de début et de fin au layoutMarginsGuide:

UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
yourView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
                                           [safe.trailingAnchor constraintEqualToAnchor:yourView.trailingAnchor],
                                           [yourView.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor],
                                           [yourView.topAnchor constraintEqualToAnchor:safe.topAnchor],
                                           [safe.bottomAnchor constraintEqualToAnchor:yourView.bottomAnchor]
                                          ]];

Veuillez également cocher l'option pour la version inférieure d'ios 11 à partir de la réponse de Krunal.

Tony TRAN
la source
Assurez-vous que vous avez déjà ajouté yourView au superView. C'est self.view Dans mon code comme un exemple simple.
Tony TRAN
6

Utiliser UIWindowou UIView« ssafeAreaInsets .bottom .top .left .right

// #available(iOS 11.0, *)
// height - UIApplication.shared.keyWindow!.safeAreaInsets.bottom

// On iPhoneX
// UIApplication.shared.keyWindow!.safeAreaInsets.top =  44
// UIApplication.shared.keyWindow!.safeAreaInsets.bottom = 34

// Other devices
// UIApplication.shared.keyWindow!.safeAreaInsets.top =  0
// UIApplication.shared.keyWindow!.safeAreaInsets.bottom = 0

// example
let window = UIApplication.shared.keyWindow!
let viewWidth = window.frame.size.width
let viewHeight = window.frame.size.height - window.safeAreaInsets.bottom
let viewFrame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight)
let aView = UIView(frame: viewFrame)
aView.backgroundColor = .red
view.addSubview(aView)
aView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
Warif Akhand Rishi
la source
3
C'est la voie à suivre si votre vue n'utilise pas la mise en page automatique.
Jonathan Cabrera
4

Utilisez des contraintes avec un format visuel et vous obtenez le respect de la zone de sécurité gratuitement.

class ViewController: UIViewController {

    var greenView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        greenView.backgroundColor = .green
        view.addSubview(greenView)
    }
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        greenView.translatesAutoresizingMaskIntoConstraints = false
        let views : [String:Any] = ["greenView":greenView]
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[greenView]-|", options: [], metrics: nil, views: views))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[greenView]-|", options: [], metrics: nil, views: views))
    }
}

résultat

AtomicBoolean
la source
2
Veuillez fournir un commentaire lors du vote à la baisse, afin que nous puissions tous savoir s'il y a un cas où cette technique ne s'applique pas. Merci!
AtomicBoolean
Cela fonctionne, j'aime aussi les solutions de format visuel! Merci! Mais cela fonctionne-t-il pour toutes les versions iOS?
PaFi
4

Extension de zone de sécurité pour Objective-C

@implementation UIView (SafeArea)

- (NSLayoutAnchor *)safeTopAnchor{

    if (@available(iOS 11.0, *)){
        return self.safeAreaLayoutGuide.topAnchor;
    } else {
        return self.topAnchor;
    }

}


- (NSLayoutAnchor *)safeBottomAnchor{

    if (@available(iOS 11.0, *)) {
        return self.safeAreaLayoutGuide.bottomAnchor;
    } else {
        return self.bottomAnchor;
    }

}

@end
perle noire
la source
cela ne fonctionne pas (Swift 4.2, iOS 12). Le résultat ignore la barre d'état
Vyachaslav Gerchicov
2

Swift 4.2 et 5.0. Supposons que vous souhaitiez ajouter des contraintes de début, de fin, de haut et de bas sur viewBg. Donc, vous pouvez utiliser le code ci-dessous.

let guide = self.view.safeAreaLayoutGuide
viewBg.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
viewBg.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
viewBg.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
viewBg.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
Gurjinder Singh
la source
1

Cette extension vous aide à contraindre un UIVIew à son superview et superview + safeArea:

extension UIView {

    ///Constraints a view to its superview
    func constraintToSuperView() {
        guard let superview = superview else { return }
        translatesAutoresizingMaskIntoConstraints = false

        topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
        leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true
        bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
        rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true
    }

    ///Constraints a view to its superview safe area
    func constraintToSafeArea() {
        guard let superview = superview else { return }
        translatesAutoresizingMaskIntoConstraints = false

        topAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.topAnchor).isActive = true
        leftAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.leftAnchor).isActive = true
        bottomAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.bottomAnchor).isActive = true
        rightAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.rightAnchor).isActive = true
    }

}
Reimond Hill
la source
0

Vous pouvez utiliser view.safeAreaInsets comme expliqué ici https://www.raywenderlich.com/174078/auto-layout-visual-format-language-tutorial-2

exemple de code (extrait de raywenderlich.com):

override func viewSafeAreaInsetsDidChange() {
  super.viewSafeAreaInsetsDidChange()

  if !allConstraints.isEmpty {
    NSLayoutConstraint.deactivate(allConstraints)
    allConstraints.removeAll()
  }

  let newInsets = view.safeAreaInsets
  let leftMargin = newInsets.left > 0 ? newInsets.left : Metrics.padding
  let rightMargin = newInsets.right > 0 ? newInsets.right : Metrics.padding
  let topMargin = newInsets.top > 0 ? newInsets.top : Metrics.padding
  let bottomMargin = newInsets.bottom > 0 ? newInsets.bottom : Metrics.padding

  let metrics = [
    "horizontalPadding": Metrics.padding,
    "iconImageViewWidth": Metrics.iconImageViewWidth,
    "topMargin": topMargin,
    "bottomMargin": bottomMargin,
    "leftMargin": leftMargin,
    "rightMargin": rightMargin]
}


let views: [String: Any] = [
  "iconImageView": iconImageView,
  "appNameLabel": appNameLabel,
  "skipButton": skipButton,
  "appImageView": appImageView,
  "welcomeLabel": welcomeLabel,
  "summaryLabel": summaryLabel,
  "pageControl": pageControl]

let iconVerticalConstraints = NSLayoutConstraint.constraints(
  withVisualFormat: "V:|-topMargin-[iconImageView(30)]",
  metrics: metrics,
  views: views)
allConstraints += iconVerticalConstraints

let topRowHorizontalFormat = """
  H:|-leftMargin-[iconImageView(iconImageViewWidth)]-[appNameLabel]-[skipButton]-rightMargin-|
  """
...
Serg
la source