Get safe area inset top and bottom heights

IosIphone XSafearealayoutguide

Ios Problem Overview


On the new iPhone X, what would be the most proper way to get both top and bottom height for the unsafe areas?

enter image description here

Ios Solutions


Solution 1 - Ios

Try this :

In Objective C

if (@available(iOS 11.0, *)) {
    UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
    CGFloat topPadding = window.safeAreaInsets.top;
    CGFloat bottomPadding = window.safeAreaInsets.bottom;
}

In Swift

if #available(iOS 11.0, *) {
    let window = UIApplication.shared.keyWindow
    let topPadding = window?.safeAreaInsets.top
    let bottomPadding = window?.safeAreaInsets.bottom
}

In Swift - iOS 13.0 and above

// Use the first element from windows array as KeyWindow deprecated

if #available(iOS 13.0, *) {
    let window = UIApplication.shared.windows.first
    let topPadding = window.safeAreaInsets.top
    let bottomPadding = window.safeAreaInsets.bottom
}

Solution 2 - Ios

To get the height between the layout guides you just do

let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height

So full frame height = 812.0, safe area height = 734.0

Below is the example where the green view has frame of guide.layoutFrame

enter image description here

Solution 3 - Ios

Swift 4, 5

To pin a view to a safe area anchor using constraints can be done anywhere in the view controller's lifecycle because they're queued by the API and handled after the view has been loaded into memory. However, getting safe-area values requires waiting toward the end of a view controller's lifecycle, like viewDidLayoutSubviews().

This plugs into any view controller:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let topSafeArea: CGFloat
    let bottomSafeArea: CGFloat

    if #available(iOS 11.0, *) {
        topSafeArea = view.safeAreaInsets.top
        bottomSafeArea = view.safeAreaInsets.bottom
    } else {
        topSafeArea = topLayoutGuide.length
        bottomSafeArea = bottomLayoutGuide.length
    }

    // safe area values are now available to use
}

I prefer this method to getting it off of the window (when possible) because it’s how the API was designed and, more importantly, the values are updated during all view changes, like device orientation changes.

However, some custom presented view controllers cannot use the above method (I suspect because they are in transient container views). In such cases, you can get the values off of the root view controller, which will always be available anywhere in the current view controller's lifecycle.

anyLifecycleMethod()
    guard let root = UIApplication.shared.keyWindow?.rootViewController else {
        return
    }
    let topSafeArea: CGFloat
    let bottomSafeArea: CGFloat

    if #available(iOS 11.0, *) {
        topSafeArea = root.view.safeAreaInsets.top
        bottomSafeArea = root.view.safeAreaInsets.bottom
    } else {
        topSafeArea = root.topLayoutGuide.length
        bottomSafeArea = root.bottomLayoutGuide.length
    }

    // safe area values are now available to use
}

Solution 4 - Ios

None of the other answers here worked for me, but this did.

var topSafeAreaHeight: CGFloat = 0
var bottomSafeAreaHeight: CGFloat = 0

  if #available(iOS 11.0, *) {
    let window = UIApplication.shared.windows[0]
    let safeFrame = window.safeAreaLayoutGuide.layoutFrame
    topSafeAreaHeight = safeFrame.minY
    bottomSafeAreaHeight = window.frame.maxY - safeFrame.maxY
  }


Solution 5 - Ios

All of the answers here are helpful, Thanks to everyone who offered help.

However as i see that that the safe area topic is a little bit confused which won’t appear to be well documented.

So i will summarize it here as mush as possible to make it easy to understand safeAreaInsets, safeAreaLayoutGuide and LayoutGuide.

In iOS 7, Apple introduced the topLayoutGuide and bottomLayoutGuide properties in UIViewController, They allowed you to create constraints to keep your content from being hidden by UIKit bars like the status, navigation or tab bar It was possible with these layout guides to specify constraints on content, avoiding it to be hidden by top or bottom navigation elements (UIKit bars, status bar, nav or tab bar…).

So for example if you wanna make a tableView starts from the top screen you have done something like that:

self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)

In iOS 11 Apple has deprecated these properties replacing them with a single safe area layout guide

Safe area according to Apple

> Safe areas help you place your views within the visible portion of the overall interface. UIKit-defined view controllers may position special views on top of your content. For example, a navigation controller displays a navigation bar on top of the underlying view controller’s content. Even when such views are partially transparent, they still occlude the content that is underneath them. In tvOS, the safe area also includes the screen’s overscan insets, which represent the area covered by the screen’s bezel.

Below, a safe area highlighted in iPhone 8 and iPhone X-series:

enter image description here

The safeAreaLayoutGuide is a property of UIView

To get the height of safeAreaLayoutGuide:

extension UIView {
   var safeAreaHeight: CGFloat {
       if #available(iOS 11, *) {
        return safeAreaLayoutGuide.layoutFrame.size.height
       }
       return bounds.height
  }
}

That will return the height of the Arrow in your picture.

Now, what about getting the top "notch" and bottom home screen indicator heights?

Here we will use the safeAreaInsets

> The safe area of a view reflects the area not covered by navigation bars, tab bars, toolbars, and other ancestors that obscure a view controller's view. (In tvOS, the safe area reflects the area not covered by the screen's bezel.) You obtain the safe area for a view by applying the insets in this property to the view's bounds rectangle. If the view is not currently installed in a view hierarchy, or is not yet visible onscreen, the edge insets in this property are 0.

The following will show the unsafe area and there distance from edges on iPhone 8 and one of iPhone X-Series.

enter image description here

enter image description here

Now, if navigation bar added

enter image description here

enter image description here

So, now how to get the unsafe area height? we will use the safeAreaInset

Here are to solutions however they differ in an important thing,

First One:

self.view.safeAreaInsets

That will return the EdgeInsets, you can now access the top and the bottom to know the insets,

Second One:

UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets

The first one you are taking the view insets, so if there a navigation bar it will be considered , however the second one you are accessing the window's safeAreaInsets so the navigation bar will not be considered

Solution 6 - Ios

Swift 5, Xcode 11.4

`UIApplication.shared.keyWindow` 

It will give deprecation warning. ''keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes' because of connected scenes. I use this way.

extension UIView {
    
    var safeAreaBottom: CGFloat {
         if #available(iOS 11, *) {
            if let window = UIApplication.shared.keyWindowInConnectedScenes {
                return window.safeAreaInsets.bottom
            }
         }
         return 0
    }
    
    var safeAreaTop: CGFloat {
         if #available(iOS 11, *) {
            if let window = UIApplication.shared.keyWindowInConnectedScenes {
                return window.safeAreaInsets.top
            }
         }
         return 0
    }
}

extension UIApplication {
    var keyWindowInConnectedScenes: UIWindow? {
        return windows.first(where: { $0.isKeyWindow })
    }
}

Solution 7 - Ios

In iOS 11 there is a method that tells when the safeArea has changed.

override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    let top = view.safeAreaInsets.top
    let bottom = view.safeAreaInsets.bottom
}

Solution 8 - Ios

safeAreaLayoutGuide When the view is visible onscreen, this guide reflects the portion of the view that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. (In tvOS, the safe area reflects the area not covered the screen's bezel.) If the view is not currently installed in a view hierarchy, or is not yet visible onscreen, the layout guide edges are equal to the edges of the view.

Then to get the height of the red arrow in the screenshot it's:

self.safeAreaLayoutGuide.layoutFrame.size.height

Solution 9 - Ios

Swift 5 Extension

This can be used as a Extension and called with: UIApplication.topSafeAreaHeight

extension UIApplication {
    static var topSafeAreaHeight: CGFloat {
        var topSafeAreaHeight: CGFloat = 0
         if #available(iOS 11.0, *) {
               let window = UIApplication.shared.windows[0]
               let safeFrame = window.safeAreaLayoutGuide.layoutFrame
               topSafeAreaHeight = safeFrame.minY
             }
        return topSafeAreaHeight
    }
}

Extension of UIApplication is optional, can be an extension of UIView or whatever is preferred, or probably even better a global function.

Solution 10 - Ios

This works for the entire view life cycle as a simple 2-line solution in Swift:

let top    = UIApplication.shared.windows[0].safeAreaInsets.top
let bottom = UIApplication.shared.windows[0].safeAreaInsets.bottom

I personally needed it in the viewDidLoad and view.safeAreaInsets isn't calculated yet.

Solution 11 - Ios

I'm working with CocoaPods frameworks and in case UIApplication.shared is unavailable then I use safeAreaInsets in view's window:

if #available(iOS 11.0, *) {
    let insets = view.window?.safeAreaInsets
    let top = insets.top
    let bottom = insets.bottom
}

Solution 12 - Ios

UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 
CGFloat fBottomPadding = window.safeAreaInsets.bottom;

Solution 13 - Ios

For SwiftUI:

Code

private struct SafeAreaInsetsKey: EnvironmentKey {
    static var defaultValue: EdgeInsets {
        UIApplication.shared.windows[0].safeAreaInsets.insets
    }
}

extension EnvironmentValues {
    
    var safeAreaInsets: EdgeInsets {
        self[SafeAreaInsetsKey.self]
    }
}

private extension UIEdgeInsets {
    
    var insets: EdgeInsets {
        EdgeInsets(top: top, leading: left, bottom: bottom, trailing: right)
    }
}

Usage

struct MyView: View {
    
    @Environment(\.safeAreaInsets) private var safeAreaInsets
    
    var body: some View {
        Text("Ciao")
            .padding(safeAreaInsets)
    }
}

Solution 14 - Ios

Or using SwiftUI GeometryReader api.

struct V: View {
    var body: some View {
        GeometryReader { geometry in
            Text("\(geometry.safeAreaInsets.top)")
            Text("\(geometry.safeAreaInsets.bottom)")
        }
    }
}


Code might not work, but illustrates the idea.

Solution 15 - Ios

Objective-C Who had the problem when keyWindow is equal to nil. Just put the code above in viewDidAppear (not in viewDidLoad)

Solution 16 - Ios

For iOS 13+/Swift 5, nothing else here worked for me but this:

    if #available(iOS 13.0, *) {
        topPadding = UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0
        bottomPadding = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
    }

Solution 17 - Ios

A more rounded approach

  import SnapKit
  
  let containerView = UIView()
  containerView.backgroundColor = .red
  self.view.addSubview(containerView)
  containerView.snp.remakeConstraints { (make) -> Void in
        make.width.top.equalToSuperView()
        make.top.equalTo(self.view.safeArea.top)
        make.bottom.equalTo(self.view.safeArea.bottom)
  }




extension UIView {
    var safeArea: ConstraintBasicAttributesDSL {
        if #available(iOS 11.0, *) {
            return self.safeAreaLayoutGuide.snp
        }
        return self.snp
    }
    
    
    var isIphoneX: Bool {
        
        if #available(iOS 11.0, *) {
            if topSafeAreaInset > CGFloat(0) {
                return true
            } else {
                return false
            }
        } else {
            return false
        }
        
    }
    
    var topSafeAreaInset: CGFloat {
        let window = UIApplication.shared.keyWindow
        var topPadding: CGFloat = 0
        if #available(iOS 11.0, *) {
            topPadding = window?.safeAreaInsets.top ?? 0
        }
        
        return topPadding
    }
    
    var bottomSafeAreaInset: CGFloat {
        let window = UIApplication.shared.keyWindow
        var bottomPadding: CGFloat = 0
        if #available(iOS 11.0, *) {
            bottomPadding = window?.safeAreaInsets.bottom ?? 0
        }
        
        return bottomPadding
    }
}

Solution 18 - Ios

For those of you who change to landscape mode, you gotta make sure to use viewSafeAreaInsetsDidChange after the rotation to get the most updated values:

private var safeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

override func viewSafeAreaInsetsDidChange() {
        if #available(iOS 11.0, *) {
            safeAreaInsets = UIApplication.shared.keyWindow!.safeAreaInsets
        }
}

Solution 19 - Ios

> Swift 4

if let window = UIApplication.shared.windows.first {
    
    let topPadding = window.safeAreaInsets.top
    let bottomPadding = window.safeAreaInsets.bottom
    
}

Use from class

class fitToTopInsetConstraint: NSLayoutConstraint {
    
    override func awakeFromNib() {
        
        if let window = UIApplication.shared.windows.first {
            
            let topPadding = window.safeAreaInsets.top
            
            self.constant += topPadding
            
        }
        
    }
}

class fitToBottomInsetConstraint: NSLayoutConstraint {
    
    override func awakeFromNib() {
        
        if let window = UIApplication.shared.windows.first {
            
            let bottomPadding = window.safeAreaInsets.bottom
            
            self.constant += bottomPadding
            
        }
        
    }
}

enter image description here

enter image description here

You will see safe area padding when you build your application.

Solution 20 - Ios

extension UIViewController {
    var topbarHeight: CGFloat {
        return
            (view.window?.safeAreaInsets.top ?? 0) +
            (view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0) +
            (self.navigationController?.navigationBar.frame.height ?? 0.0)
    }
}

Solution 21 - Ios

Here's a free function based on other answers that should be callable once your rootController is layed out from anywhere. You can use it as a free standing function.

    func safeAreaInsets() -> UIEdgeInsets? {
    (UIApplication
        .shared
        .keyWindow?
        .rootViewController)
        .flatMap {
            if #available(iOS 11.0, *) {
                return $0.view.safeAreaInsets
            } else {
                return .init(
                    top: $0.topLayoutGuide.length,
                    left: .zero,
                    bottom: $0.bottomLayoutGuide.length,
                    right: .zero
                )
            }
        }
}

Solution 22 - Ios

Here is a simple answer to find safe area height for all iphone

let window = UIApplication.shared.windows[0]

let SafeAreaHeight = window.safeAreaLayoutGuide.layoutFrame.size.height

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
QuestionTullebView Question on Stackoverflow
Solution 1 - Iosuser6788419View Answer on Stackoverflow
Solution 2 - IosLadislavView Answer on Stackoverflow
Solution 3 - IosliquidView Answer on Stackoverflow
Solution 4 - IosScottyBladesView Answer on Stackoverflow
Solution 5 - Iosmojtaba al moussawiView Answer on Stackoverflow
Solution 6 - Iosuser1039695View Answer on Stackoverflow
Solution 7 - IosMartinView Answer on Stackoverflow
Solution 8 - IosmalhalView Answer on Stackoverflow
Solution 9 - IosTheodoreView Answer on Stackoverflow
Solution 10 - IosMichaelView Answer on Stackoverflow
Solution 11 - IosTai LeView Answer on Stackoverflow
Solution 12 - IosPeterView Answer on Stackoverflow
Solution 13 - IosLorenzo FiamingoView Answer on Stackoverflow
Solution 14 - IosIlyaView Answer on Stackoverflow
Solution 15 - IosDmitryShapkinView Answer on Stackoverflow
Solution 16 - IosJapesView Answer on Stackoverflow
Solution 17 - IosjohndpopeView Answer on Stackoverflow
Solution 18 - IosOz ShabatView Answer on Stackoverflow
Solution 19 - IosSaharat SittipanyaView Answer on Stackoverflow
Solution 20 - IosSerg SmykView Answer on Stackoverflow
Solution 21 - IossmileBotView Answer on Stackoverflow
Solution 22 - Iosvignesh sha VigneshView Answer on Stackoverflow