How do I use Safe Area Layout programmatically?

IosSwiftIphone XSafearealayoutguide

Ios Problem Overview


Since I don't use storyboards to create my views, I was wondering if there's the "Use Safe Area Guides" option programmatically or something like that.

I've tried to anchor my views to

view.safeAreaLayoutGuide

but they keep overlapping the top notch in the iPhone X simulator.

Ios Solutions


Solution 1 - Ios

Here is sample code (Ref from: Safe Area Layout Guide):
If you create your constraints in code use the safeAreaLayoutGuide property of UIView to get the relevant layout anchors. Let’s recreate the above Interface Builder example in code to see how it looks:

Assuming we have the green view as a property in our view controller:

private let greenView = UIView()

We might have a function to set up the views and constraints called from viewDidLoad:

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

Create the leading and trailing margin constraints as always using the layoutMarginsGuide of the root view:

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

Now, unless you are targeting iOS 11 and later, you will need to wrap the safe area layout guide constraints with #available and fall back to top and bottom layout guides for earlier iOS versions:

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

Result:

enter image description here

enter image description here


Here is Apple Developer Official Documentation for Safe Area Layout Guide


Safe Area is required to handle user interface design for iPhone-X. Here is basic guideline for How to design user interface for iPhone-X using Safe Area Layout

Solution 2 - Ios

I'm actually using an extension for it and controlling if it is iOS 11 or not.

extension UIView {

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

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

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

  var safeBottomAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return safeAreaLayoutGuide.bottomAnchor
    }
    return bottomAnchor
  }
}

Solution 3 - Ios

SafeAreaLayoutGuide is UIView property,

> The top of the safeAreaLayoutGuide indicates the unobscured top edge > of the view (e.g, not behind > the status bar or navigation bar, if present). Similarly for the other edges.

Use safeAreaLayoutGuide for avoid our objects clipping/overlapping from rounded corners, navigation bars, tab bars, toolbars, and other ancestor views.

We can create safeAreaLayoutGuide object & set object constraints respectively.

Constraints for Portrait + Landscape is -

Portrait image

Landscape image

        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

Solution 4 - Ios

For those of you who use SnapKit, just like me, the solution is anchoring your constraints to view.safeAreaLayoutGuide like so:

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

Solution 5 - Ios

I'm using this instead of add leading and trailing margin constraints to the 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]
                                          ]];

Please also check the option for lower version of ios 11 from Krunal's answer.

Solution 6 - Ios

Use UIWindow or UIView's safeAreaInsets .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]
    

Solution 7 - Ios

Swift 4.2 and 5.0. Suppose you wan to add Leading, Trailing, Top and Bottom constraints on viewBg. So, you can use the below code.

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

Solution 8 - Ios

Use constraints with visual format and you get respect for the safe area for free.

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

result

Solution 9 - Ios

Safe area extension For 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

Solution 10 - Ios

This extension helps you to constraint a UIVIew to its superview and 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
    }
    
}

Solution 11 - Ios

You can use view.safeAreaInsets as explained here https://www.raywenderlich.com/174078/auto-layout-visual-format-language-tutorial-2

code sample (taken from 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-|
  """
...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPhillipView Question on Stackoverflow
Solution 1 - IosKrunalView Answer on Stackoverflow
Solution 2 - IosAlperView Answer on Stackoverflow
Solution 3 - IosJackView Answer on Stackoverflow
Solution 4 - IosPhillipView Answer on Stackoverflow
Solution 5 - IosTony TRANView Answer on Stackoverflow
Solution 6 - IosWarif Akhand RishiView Answer on Stackoverflow
Solution 7 - IosGurjinder SinghView Answer on Stackoverflow
Solution 8 - IosAtomicBooleanView Answer on Stackoverflow
Solution 9 - Iosblack_pearlView Answer on Stackoverflow
Solution 10 - IosReimond HillView Answer on Stackoverflow
Solution 11 - IosSergView Answer on Stackoverflow