Reputation: 33
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 |
---|---|
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
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 UIView
s 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:
For further reading, I have a blog post on this topic.
Upvotes: 3