José Miguel Lapa
José Miguel Lapa

Reputation: 33

How can I add the safe area to the view height?

I am creating a top "banner" and I want it to cover the safe area and some more.
I can't seem to find how to cover the Safe area.

The first picture is the desired effect, the second is the effect on notched iPhones.

Desired Effect What's Happening
Text stays clear of the status bar on notchless iPhones Text touches the notch on notched iPhones

How can I add the safe area to the desired value of height?

Code:

let shadowView = UIView( frame: CGRect( x: -10, y: -20, width: (view.frame.width+20), height: 90 ) )
        view.addSubview( shadowView )
        shadowView.backgroundColor = .clear
        shadowView.layer.shadowColor = UIColor.black.cgColor
        shadowView.layer.shadowOpacity = 0.3
        shadowView.layer.shadowOffset = CGSize(width: 5, height: 3)
        shadowView.layer.shadowRadius = 4.0
let titleView = UIView( frame: CGRect( x: 0, y: 0, width: ( shadowView.frame.width ), height: 90 ) )
        shadowView.addSubview( titleView )
        titleView.backgroundColor = .systemGreen
        titleView.layer.cornerRadius = 45
        titleView.layer.masksToBounds = true

Upvotes: 3

Views: 2665

Answers (1)

aheze
aheze

Reputation: 30318

The problem is here:

let shadowView = UIView( frame: CGRect( x: -10, y: -20, width: (view.frame.width+20), height: 90 ) )

You are hardcoding the frame - don't do this! Well, a height of 90 is fine. But the x: -10, y: -20, width: (view.frame.width+20) is terrible. Not all phones are the same size.

Technically, you could calculate the safe area inset height as NoeOnJupiter commented, but this is still pretty bad. What happens when the user rotates their device, and the notch moves? It sounds like a lot of work.

What you want is Auto Layout and the Safe Area. With Auto Layout, simply define some constraints, then watch your UIViews look great on all screen sizes. Then, the Safe Area defines what parts of the screen are "safe," meaning "not covered by notches or rounded screen corners."

So, you can pin shadowView to the edges of the screen (beyond the notch/safe area), and add the .systemGreen background color. Then, make titleView 90 points high, pinning it vertically to shadowView. Just note how titleView.topAnchor is pinned to shadowView.safeAreaLayoutGuide.topAnchor — this makes it stay clear of the notch.

let shadowView = UIView() /// green background with shadow and corner radius
shadowView.translatesAutoresizingMaskIntoConstraints = false
shadowView.backgroundColor = .systemGreen /// put the background color here instead of on `titleView`, because this view stretches beyond the notch
shadowView.layer.shadowColor = UIColor.black.cgColor
shadowView.layer.shadowOpacity = 0.3
shadowView.layer.shadowOffset = CGSize(width: 5, height: 3)
shadowView.layer.shadowRadius = 4.0
shadowView.layer.cornerRadius = 45
shadowView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] /// only round the bottom corners
view.addSubview(shadowView)

let titleView = UIView() /// container for title label.
titleView.translatesAutoresizingMaskIntoConstraints = false
shadowView.addSubview(titleView)

let titleLabel = UILabel() /// the title label itself
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleView.addSubview(titleLabel)
titleLabel.text = "Catalogues"
titleLabel.font = UIFont.systemFont(ofSize: 36, weight: .medium)

NSLayoutConstraint.activate([
    
    /// constrain `shadowView`
    shadowView.topAnchor.constraint(equalTo: view.topAnchor),
    shadowView.rightAnchor.constraint(equalTo: view.rightAnchor),
    shadowView.leftAnchor.constraint(equalTo: view.leftAnchor),
    
    
    /// constrain `titleView`
    titleView.topAnchor.constraint(equalTo: shadowView.safeAreaLayoutGuide.topAnchor), /// most important part!
    titleView.heightAnchor.constraint(equalToConstant: 90), /// will also stretch `shadowView` vertically
    titleView.rightAnchor.constraint(equalTo: shadowView.rightAnchor),
    titleView.leftAnchor.constraint(equalTo: shadowView.leftAnchor),
    titleView.bottomAnchor.constraint(equalTo: shadowView.bottomAnchor),
    
    /// constrain `titleLabel`
    titleLabel.centerXAnchor.constraint(equalTo: titleView.centerXAnchor),
    titleLabel.centerYAnchor.constraint(equalTo: titleView.centerYAnchor)
])

Result:

iPhone 8 iPhone 12
Green background flows to the edge of the screen, but the text label maintains a gap Green background flows to the edge of the screen, but the text label stops at the notch

For further reading, I have a blog post on this topic.

Upvotes: 3

Related Questions