This is my first question on here.
I want padding at the edges of this View:
struct Test3: View {
var body: some View {
VStack {
ZStack {
Color.blue
Image("SplashPage")
.resizable()
.scaledToFill()
}
ZStack {
Color.gray
Button("touch me", action: {})
}
.frame(width: .infinity, height: 48)
}
.padding(24)
}
}
I've searched here and thought this post would solve my problems:
.clipped()
-> Nope.
I've searched online. Asked ChatGPT. Looked at hackingwithswift. Looking for "mask image SwiftUI" and "SwiftUI clip image to ZStack"
... nope.
I've been trying to nail down what I'm doing wrong and I just can't figure it out:
I have a VStack which contains 2 ZStacks. This VStack has padding of 24 all around.
In the first VStack, I added an image that is .resizable()
and .scaledToFill()
I expected the image to be clipped respecting the 24 point padding. I expect this to respect the padding on an iPhone SE and an iPhone 14 Pro or whatever large emulator.
The second VStack has a button with .frame(width: .infinity, height: 48)
. If I comment this line out, then the image behaves as expected!
This problem is being caused by the scaledToFill()
on the Image
. It seems to fill the full screen width and ignores the padding at the sides.
You found it was working when you removed the .frame
modifier from the second ZStack
. I am not sure why this helped, but there was a separate issue here too. Setting a width
of .infinity
is invalid (and I was seeing errors in the console). It needs to be maxWidth
instead.
So here are a couple of workarounds for the image scaling:
1. Surround the Image
with a GeometryReader
A GeometryReader
can be used to measure the space that is really available and this size can be set on the Image
. After setting the size, it also needs to be .clipped()
.
VStack {
ZStack {
Color.blue
GeometryReader { proxy in
Image(systemName: "ladybug.fill") // "SplashPage"
.resizable()
.scaledToFill()
.foregroundStyle(.orange)
.frame(width: proxy.size.width, height: proxy.size.height)
.clipped()
}
}
ZStack {
Color.gray
Button("touch me", action: {})
}
.frame(maxWidth: .infinity)
.frame(height: 48)
}
.padding(24)
2. Show the Image
as an overlay
The Color
that you are using in the background will expand to fill all of the space available. If the Image
is applied as an overlay to this Color
then it will adopt the same frame size. The surrounding ZStack
is then redundant and can be omitted.
With this approach, the .clipped()
modifier needs to be applied after the overlay to the Color
, not to the Image
.
VStack {
Color.blue
.overlay {
Image(systemName: "ladybug.fill") // "SplashPage"
.resizable()
.scaledToFill()
.foregroundStyle(.orange)
}
.clipped()
ZStack {
Color.gray
Button("touch me", action: {})
}
.frame(maxWidth: .infinity)
.frame(height: 48)
}
.padding(24)
The result is the same in both cases: