17

I'm trying programmatically set constraints for some labels, buttons, and text fields relative to the height and width of the safe area. For example, I'm hoping to set the distance from the label from the top of the safe area to 10% of the safe area's height.

How can I retrieve the height and width of the safe area?

Is this a sound approach? My thought process was that my screen would then adjust automatically regardless of iOS device.

Thanks for the help!

Nick
  • 4,820
  • 18
  • 31
  • 47
code_monkey
  • 215
  • 1
  • 2
  • 10
  • Possible duplicate of [Get safe area inset top and bottom heights](https://stackoverflow.com/questions/46829840/get-safe-area-inset-top-and-bottom-heights) – kiwisip Feb 28 '18 at 13:49

4 Answers4

23

You can get the height by storing the frame of the safe area. safeAreaLayoutGuide has a property named layoutFrame.

let frame = self.view.safeAreaLayoutGuide.layoutFrame
print(frame.height)
print(frame.width)

I made an UIView extension that creates a read-only property called safeAreaFrame.

safeAreaFrame will be the frame of the safe area if the iOS version is 11 or greater.

Otherwise, it will be bounds (just the frame of the view).

extension UIView {
    public var safeAreaFrame: CGRect {
        if #available(iOS 11, *) {
            return safeAreaLayoutGuide.layoutFrame
        }
        return bounds
    }
}

Example:

let frame = self.view.safeAreaFrame 
print(frame.height)
print(frame.width)
codeherk
  • 1,609
  • 15
  • 24
  • 2
    this one only works in viewDidLayoutSubview, I want to store height in a variable. is it safe to store value in variable in viewDidLayouSubviews – Sohaib Siddique Sep 03 '19 at 06:49
16
if #available(iOS 11.0, *) {
    if let window = UIApplication.shared.keyWindow {
        let safeAreaBottom = window.safeAreaInsets.bottom
        let safeAreaLeft = window.safeAreaInsets.left
        let safeAreaRight = window.safeAreaInsets.right
        let safeAreaTop = window.safeAreaInsets.top
    }
}
maxwell
  • 3,788
  • 6
  • 26
  • 40
9
override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()
    let safeAreaHeight = view.safeAreaLayoutGuide.layoutFrame.height
    let safeAraeWidth = view.safeAreaLayoutGuide.layoutFrame.width
}

Remember to do this in viewDidLayoutSubviews function or setup your constraints in any function and then call that function inside viewDidLayoutSubviews.

viewDidLoad won't give you the safe area frames, as it runs before loading the view.

סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Simran Singh
  • 445
  • 6
  • 8
0

Let's say, you have a label which height is calculated based on it's superview height dimension.

var label: UILabel!

You can anchor the height of the label like

  // enable auto-layout
  label.translatesAutoresizingMaskIntoConstraints = false 
  // add label into super view
  view.addSubview(label) 
  // calculate height and activate constraint
  label.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor, constant: 0.1).isActive = true // calculate height based on super view safe area layout guide

You can active constraints using the following method also (instead of .isActive = true):

  let superHeight = view.frame.height
  NSLayoutConstraint.activate([
        label.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor, constant: 0.1), // 10% of super view height
        label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: superHeight * 0.1) 
  ])

Set the other constraints as per your requirements.

Let's take an example:

private let testLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.text = "Test"
    label.numberOfLines = 0 // can be multiple line based on content
    label.textAlignment = .center
    label.textColor = .gray
    return label
}()

private func labelSetup() {
    view.addSubview(testLabel)
    let heightSuperView = view.frame.height
    NSLayoutConstraint.activate([
        testLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8),
        testLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: heightSuperView*0.1),
        testLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8)
    ])
}

call labelSetup() method inside viewDidLoad(), You will get the expected output.

Ashis Laha
  • 2,176
  • 3
  • 16
  • 17