Problem: when using rotation3DEffect over a ZStack that has modifiers: .background(.ultraThinMaterial) and .shadow, ultraThinMaterial texture dissapears.
- If shadow modifier is removed from ZStack(check ViewWithNoShadow in the following code) the glitch does not happen.
- If we use rotationEffect instead of rotation3DEffect and keep the shadow(check ViewWithShadow in the following code), the glitch does not happen.
CODE:
ZStack with shadow modifier and ultraThinMaterial as background
struct ViewWithShadow: View {
private let shadowValues: (radius: CGFloat, x: CGFloat, y: CGFloat) = (1,2,2)
var body: some View {
ZStack {
}
.frame(maxHeight: .infinity)
.frame(maxWidth: .infinity)
.background(.ultraThinMaterial)
.cornerRadius(20)
.shadow(color: .primary.opacity(0.3), radius: shadowValues.radius, x: shadowValues.x, y: shadowValues.y)
}
}
ZStack with no shadow modifier and ultraThinMaterial as background
struct ViewWithNoShadow: View {
private let shadowValues: (radius: CGFloat, x: CGFloat, y: CGFloat) = (1,2,2)
var body: some View {
ZStack {
}
.frame(maxHeight: .infinity)
.frame(maxWidth: .infinity)
.background(.ultraThinMaterial)
.cornerRadius(20)
}
}
ContentView is a simple container that decides which view to show between ViewWithShadow and ViewWithNoShadow
struct ContentView: View {
let hasShadow: Bool
var body: some View {
ZStack {
Color.teal
VStack(spacing: 10) {
RoundedRectangle(cornerRadius: 10)
.frame(width: 200, height: 100)
RoundedRectangle(cornerRadius: 10)
.fill(Color.red)
.frame(width: 200, height: 100)
}
.padding()
if hasShadow {
ViewWithShadow()
.frame(height: 300)
.padding()
} else {
ViewWithNoShadow()
.frame(height: 300)
.padding()
}
}
}
}
AppMainContainer shows an ScrollView with the three use cases mentioned above
struct AppMainContainer: View {
@State var isContentAAside: Bool = false
@State var isContentBAside: Bool = false
@State var isContentCAside: Bool = false
var body: some View {
ScrollView(showsIndicators: false) {
VStack {
GroupBox("rotation3DEffect, ViewWithShadow") {
button(text: "Click A") {
isContentAAside.toggle()
}
ContentView(hasShadow: true)
.scaleEffect(isContentAAside ? 0.92 : 1)
.rotation3DEffect(isContentAAside ? .init(degrees: -20) : .init(degrees: 0), axis: isContentAAside ? (x: 0, y: -2, z: -0.3) : (x: 0, y: 0, z: 0))
.offset(x: isContentAAside ? UIScreen.main.bounds.width - 150 : 0, y: isContentAAside ? 20 : 0)
.animation(.easeInOut, value: isContentAAside)
}
GroupBox("rotationEffect, ViewWithShadow") {
button(text: "Click B") {
isContentBAside.toggle()
}
ContentView(hasShadow: true)
.scaleEffect(isContentBAside ? 0.92 : 1)
.rotationEffect(isContentBAside ? .init(degrees: -20) : .init(degrees: 0))
.offset(x: isContentBAside ? UIScreen.main.bounds.width - 220 : 0, y: isContentBAside ? 10 : 0)
.animation(.easeInOut, value: isContentBAside)
}
GroupBox("rotation3DEffect, ViewWithNoShadow") {
button(text: "Click C") {
isContentCAside.toggle()
}
ContentView(hasShadow: false)
.scaleEffect(isContentCAside ? 0.92 : 1)
.rotation3DEffect(isContentCAside ? .init(degrees: -20) : .init(degrees: 0), axis: isContentCAside ? (x: 0, y: -2, z: -0.3) : (x: 0, y: 0, z: 0))
.offset(x: isContentCAside ? UIScreen.main.bounds.width - 220 : 0, y: isContentCAside ? 10 : 0)
.animation(.easeInOut, value: isContentCAside)
}
}
}
}
func button(text: String, action: @escaping () -> Void) -> some View {
Button {
action()
} label: {
Text(text)
}
}
}
App class, entry point
import SwiftUI
@main
struct GlitchApp: App {
var body: some Scene {
WindowGroup {
AppMainContainer()
}
}
}
With the code above you can reproduce the following glitch:
Expected Result: Use rotation3DEffect on ViewWithShadow without having any weird behaviour.
Any idea of why does this happen and how to solve it?. Thanks in advance!
