Search code examples
animationswiftuiscaletransition

SwiftUI transition modifier not working as I believed


I believed that this code would result in the background darkener fading in while the red square scales in, but what actually happens is that both simply fade in. Why does the .transition(.scale) modifier on the red square not cause it to scale in when it is added to the view hierarchy?

struct DemoView: View {
    
    @State
    private var flag = false
    
    var body: some View {
        Button(
            action: {
                withAnimation {
                    flag = true
                }
            },
            label: {
                Color.blue
            }
        )
            .overlay(
                ZStack {
                    if flag {
                        Button(
                            action: {
                                withAnimation {
                                    flag = false
                                }
                            },
                            label: {
                                Color
                                    .black
                                    .opacity(0.5)
                            }
                        )
                            .transition(.opacity)
                            .overlay(
                                Color.red
                                    .frame(width: 200, height: 200)
                                    .transition(.scale)
                            )
                    }
                }
            )
            .buttonStyle(.plain)
    }
}

Solution

  • Cascading does not work here, ie. transition works on read per-view appear. The red color box in really not appeared here, it is appears as part of built parent view.

    Here is a fixed part to give effect as you wanted (as I understood).

    demo

    Tested with Xcode 13.2 / iOS 15.2

    .overlay(
        ZStack {
            if flag {
                Button(
                    action: {
                        withAnimation {
                            flag = false
                        }
                    },
                    label: {
                        Color
                            .black
                            .opacity(0.5)
                    }
                )
                .transition(.opacity)
            }
            if flag {
                Color.red
                    .frame(width: 200, height: 200)
                    .transition(.scale)
            }
        }
        .animation(.default, value: flag)