Wie verwende ich das Safe Area Layout programmgesteuert?

98

Da ich zum Erstellen meiner Ansichten keine Storyboards verwende, habe ich mich gefragt, ob es die Option "Sicherheitsbereichshandbücher verwenden" programmgesteuert oder ähnliches gibt.

Ich habe versucht, meine Ansichten zu verankern

view.safeAreaLayoutGuide

Aber sie überlappen immer wieder die Spitzenklasse im iPhone X-Simulator.

Phillip
quelle
1
Keine dokumentierte Bool-Eigenschaft dazu laut: developer.apple.com/documentation/uikit/uiview/…
dvp.petrov
Wie wäre es mit view.safeAreaInsets? Hast du das versucht?
Karthikeyan Bose
@KarthikeyanBose ja ich habe leider ohne Glück gemacht.
Phillip
funktioniert bei mir. Wie sieht Code aus
Daniel Springer

Antworten:

153

Hier ist ein Beispielcode (siehe: Safe Area Layout Guide ):
Wenn Sie Ihre Einschränkungen im Code erstellen, verwenden Sie die Eigenschaft safeAreaLayoutGuide von UIView, um die relevanten Layoutanker abzurufen. Lassen Sie uns das obige Interface Builder-Beispiel im Code neu erstellen, um zu sehen, wie es aussieht:

Angenommen, wir haben die grüne Ansicht als Eigenschaft in unserem Ansichtscontroller:

private let greenView = UIView()

Möglicherweise haben wir eine Funktion zum Einrichten der Ansichten und Einschränkungen, die von viewDidLoad aufgerufen werden:

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

Erstellen Sie die Einschränkungen für führende und nachfolgende Ränder wie immer mit dem layoutMarginsGuide der Stammansicht:

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

Sofern Sie nicht auf iOS 11 und höher abzielen, müssen Sie jetzt die Einschränkungen der Layout-Anleitung für sichere Bereiche mit #available umschließen und für frühere iOS-Versionen auf die Layout-Anleitungen oben und unten zurückgreifen:

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)
   ])
}

Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein


Hier ist die offizielle Dokumentation von Apple Developer für das Layout von sicheren Bereichen


Für die Gestaltung der Benutzeroberfläche des iPhone-X ist ein sicherer Bereich erforderlich. Hier finden Sie grundlegende Richtlinien zum Entwerfen der Benutzeroberfläche für das iPhone-X mithilfe des Safe Area Layout

Krunal
quelle
2
Wäre es möglich, dies auch in Ziel C anzugeben? Es sieht aus wie genau das, was ich brauche
Tom Hammond
6
@ TomHammond Hier ist in Objective-C für Sie stackoverflow.com/a/47076040/5638630
Krunal
2
@ZonilyJame Nun zu Ihrer Anfrage - SaFeAreaLayout ist ein iOS-spezifisches Framework (nicht gerätespezifisch für iPhoneX). Es ersetzt das Top-Bottom-Layout-Handbuch in iOS 11, daher müssen wir die Bedingung für iOS verwenden / festlegen, nicht für das Gerät. SafeAreaLayout kümmert sich um Designs für alle Arten von Geräten (iPhone-X und andere). Sie können mich um weitere Details bitten, wenn Sie noch Fragen / Verwirrung haben.
Krunal
3
Genial. Vielen Dank :)
Rajamohan S
1
Warum wird es als richtige Antwort akzeptiert? Ich habe versucht, eine konkrete Position einzurichten - das Ergebnis ist völlig zufällig - die Steuerung befindet sich an einer unvorhersehbaren Position!
Vyachaslav Gerchicov
82

Ich benutze tatsächlich eine Erweiterung dafür und steuere, ob es ios 11 ist oder nicht.

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
quelle
Es ist so ein einfacher Weg und macht den Trick. Danke für die Idee.
Alper_k
Dies ist eine so einfache und doch schöne Möglichkeit! Beachten Sie die Verwendung von self.safeAreaLayoutGuideanstelle von self.layoutMarginsGuide. Der in dieser Antwort verwendete Safe funktionierte korrekt, damit ich im sicheren Bereich bleiben konnte! Eine Sache, die ich vorschlagen würde, zu ändern, wäre, leadingAnchorund trailingAnchoranstelle von leftAnchorund zu verwenden rightAnchor. Bravo!
Pranoy C
22

SafeAreaLayoutGuideist UIViewEigentum,

Der obere Rand des safeAreaLayoutGuide zeigt den freien oberen Rand der Ansicht an (z. B. nicht hinter der Statusleiste oder Navigationsleiste, falls vorhanden). Ähnliches gilt für die anderen Kanten.

Verwenden Sie diese Option, safeAreaLayoutGuideum zu vermeiden, dass unsere Objekte von abgerundeten Ecken, Navigationsleisten, Registerkartenleisten, Symbolleisten und anderen Ahnenansichten abgeschnitten / überlappt werden.

Wir können safeAreaLayoutGuideObjekt- bzw. Objektbeschränkungen erstellen .

Einschränkungen für Portrait + Landscape sind -

Porträtbild

Landschaftsbild

        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
quelle
2
Machen Sie niemals Setup-Einschränkungen in der viewDidAppear, es sei denn, Sie wissen genau, was Sie tun. viewDidAppearwird mehrmals aufgerufen und daher werden Ihre Einschränkungen bei jedem Aufruf dupliziert.
Yevhen Dubinin
Ja! , Bearbeitete Antwort, können Sie als Anwendungsfall verwenden.
Jack
14

Für diejenigen unter Ihnen, die SnapKit verwenden , genau wie ich, besteht die Lösung darin, Ihre Einschränkungen so zu verankern view.safeAreaLayoutGuide:

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
quelle
1
Gute Antwort. Um es etwas kompakter zu machen, könnte man sagen: if #available (iOS 11.0, *) {make.edges.equalTo (view.safeAreaLayoutGuide.snp.margins)}
Don Miguel
7

Ich verwende dies, anstatt dem layoutMarginsGuide Einschränkungen für führende und nachfolgende Ränder hinzuzufügen:

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]
                                          ]];

Bitte überprüfen Sie auch die Option für eine niedrigere Version von ios 11 aus Krunals Antwort.

Tony TRAN
quelle
Stellen Sie sicher, dass Sie Ihre Ansicht bereits zur Superansicht hinzufügen. Es ist self.view In meinem Code als einfaches Beispiel.
Tony TRAN
6

Verwenden Sie UIWindowoder 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
quelle
3
Dies ist der richtige Weg, wenn Ihre Ansicht kein AutoLayout verwendet.
Jonathan Cabrera
4

Wenn Sie Einschränkungen mit visuellem Format verwenden, erhalten Sie kostenlos Respekt für den sicheren Bereich.

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))
    }
}

Ergebnis

AtomicBoolean
quelle
2
Bitte geben Sie beim Abstimmen einen Kommentar ab, damit wir alle erfahren können, ob es einen Fall gibt, in dem diese Technik nicht angewendet wird. Vielen Dank!
AtomicBoolean
Das funktioniert, ich mag auch die visuellen Formatlösungen! Vielen Dank! Aber funktioniert das für alle iOS-Versionen?
PaFi
4

Safe Area Extension Für 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
schwarze Perle
quelle
es funktioniert nicht (Swift 4.2, iOS 12). Das Ergebnis ignoriert die Statusleiste
Vyachaslav Gerchicov
2

Swift 4.2 und 5.0. Angenommen, Sie möchten in viewBg die Einschränkungen Leading, Trailing, Top und Bottom hinzufügen. Sie können also den folgenden Code verwenden.

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
quelle
1

Mit dieser Erweiterung können Sie eine UIVIew auf ihre Übersicht und Aufsicht + safeArea beschränken:

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
quelle
0

Sie können view.safeAreaInsets wie hier beschrieben verwenden: https://www.raywenderlich.com/174078/auto-layout-visual-format-language-tutorial-2

Codebeispiel (entnommen aus 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
quelle