Behavior of Image and ignoresSafeArea

Consider this 400x800 image:

I would expect the background image in the following code to fill the entire screen:

struct ContentView: View {
        
    var body: some View {
        VStack {
            
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background {
            Image(.background)
                .resizable()
                .scaledToFill()
                .ignoresSafeArea()
        }
    }
    
}

But there's a small gap in the bottom:

Swapping the order of scaledToFill and ignoresSafeArea fills this gap:

Image(.background)
    .resizable()
    .ignoresSafeArea()
    .scaledToFill()

Why?

Ignoring specific edges is more problematic:

struct ContentView: View {
        
    var body: some View {
        VStack {
            
        }
        .statusBarHidden()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .border(.black)
        .background {
            Image(.background)
                .resizable()
                .ignoresSafeArea(edges: .top)
                .scaledToFill()
        }
    }
    
}

My solution here was to use the Image as an overlay of a Rectangle.

Rectangle()
    .overlay {
        Image(.background)
            .resizable()
            .scaledToFill()
    }
    .clipped()
    .ignoresSafeArea(edges: .top)

Is there a better way to achieve this?

I wonder if someone from SwiftUI Team could help me to better undestand Image behavior regardless ignoresSafeArea.

Answered by darkpaw in 847004022

When you do this:

Image(.background)
    .resizable()
    .scaledToFill()
    .ignoresSafeArea()

You're telling SwiftUI that the image can be resized, and you want to scale it to fill its parent, so SwiftUI scales the image. However, at this point, SwiftUI doesn't know that the image can ignore the safe areas, so the parent view at that point isn't ignoring safe areas. That's why you get the white bit at the bottom.

I took your image and added a red border and yellow squares in the corners so I could see where the image was stretched/resized and positioned. With the code above, you get this:

So you can see it was resized to fit inside the parent view, and doesn't ignore the safe areas.

For your needs, just remove the .scaledToFill() modifier, you don't need it:

Image(.background)
    .resizable()
    .ignoresSafeArea()

This yields:

If your ultimate need is to get that last image with just the white bit at the bottom, the fix again is to remove the scaling:

Image(.background)
    .resizable()
    .ignoresSafeArea(edges: .top)

When you do this:

Image(.background)
    .resizable()
    .scaledToFill()
    .ignoresSafeArea()

You're telling SwiftUI that the image can be resized, and you want to scale it to fill its parent, so SwiftUI scales the image. However, at this point, SwiftUI doesn't know that the image can ignore the safe areas, so the parent view at that point isn't ignoring safe areas. That's why you get the white bit at the bottom.

I took your image and added a red border and yellow squares in the corners so I could see where the image was stretched/resized and positioned. With the code above, you get this:

So you can see it was resized to fit inside the parent view, and doesn't ignore the safe areas.

For your needs, just remove the .scaledToFill() modifier, you don't need it:

Image(.background)
    .resizable()
    .ignoresSafeArea()

This yields:

If your ultimate need is to get that last image with just the white bit at the bottom, the fix again is to remove the scaling:

Image(.background)
    .resizable()
    .ignoresSafeArea(edges: .top)

I think my solid color image was a bad example because it works fine with just resizable() 😅 But a picture would be distorted without scaledToFill(). I've found another solution using an alignment value of background that is opposed to the ignored edges:

.background(alignment: .bottom) {
    Image(.background)
        .resizable()
        .ignoresSafeArea(edges: .top)
        .scaledToFill()
}

Yeah, if it's not a solid colour it would need the scaling, but hopefully it makes sense to you now? The ordering of the modifiers is important.

Behavior of Image and ignoresSafeArea
 
 
Q