I have views with shadows with a big shadow blur. I want the shadow to go behind the other views. Tried some stuff with zIndexing, but nothing seems to work. This is what is happening:
Simplified code:
import SwiftUI
struct TestBackgroundView: View {
var body: some View {
VStack {
Color.white.frame(height: 80)
.shadow(color: .red, radius: 40)
Color.white.frame(height: 80)
.shadow(color: .red, radius: 40)
}
}
}
#Preview {
TestBackgroundView()
}
In my own code, the views are buttons, where the button have a background shadow, that also changes based on the pressed state.
I've tried to overlay 2 VStacks in a ZStack, where de background is only on the bottom vstack. But then I cannot have a clear color to place the shadow on, so my animation of the button looks off, as the background of the first VStack is still visible.
struct TestBackgroundView: View {
var body: some View {
ZStack {
VStack {
Color.white.frame(height: 80)
.shadow(color: .red, radius: 40)
Color.white.frame(height: 80) // Color.clear will not show shadow
.shadow(color: .red, radius: 40)
}
VStack {
Color.white.frame(height: 80)
Color.black.opacity(0.8) // Simulating pressed state
.padding()
.frame(height: 80)
}
}
}
}
Any other solutions possible?
By default, a shadow will only cover views that came earlier in the layout. Views that follow in the layout will cover the shadow of an earlier view. So the problem only exists for the shadow of the second view in the VStack
.
If you know the spacing of the VStack
then you could use a .mask
to stop the shadow from going over the preceding view:
apply the mask with alignment: .top
use negative padding to allow the mask to extend into the space between the views
apply a generous height to the mask, to give enough space for the shadow below the view.
VStack(spacing: 10) {
Color.white
.frame(height: 80)
.shadow(color: .red, radius: 40)
Color.white
.frame(height: 80)
.shadow(color: .red, radius: 40)
.mask(alignment: .top) {
Rectangle()
.padding(.top, -10) // VStack spacing
.frame(height: 200, alignment: .top)
}
}
If you didn't know the height of the view concerned then you could use a GeometryReader
to measure it:
// (second case)
Color.white
.frame(height: 80)
.shadow(color: .red, radius: 40)
.mask(alignment: .top) {
GeometryReader { proxy in
Rectangle()
.padding(.top, -10) // VStack spacing
.frame(height: proxy.size.height + 100, alignment: .top)
}
}