Simple SwiftUI transition that doesn't seem to be working

Viewed 433

I've been trying to get a comprehensive understanding of how Animations and Transitions work in SwiftUI.

I've been experimenting with different transitions and animations all day but one transition I want isn't working. I'll first show the code and then explain what sort of transition I want.

struct Test: View {
    @State private var pressed = false // Controls whether the tower is shown or not.

    var body: some View {
        VStack {
            Button(pressed ? "Press me to hide tower" : "Press me to show tower") {  // Controls truth value of the "pressed" variable above.
                withAnimation(.easeInOut(duration: 5)) {  // I've set the duration to 5 because I want to see the animation in slow-motion.
                    self.pressed.toggle()  // Toggles truth value of "pressed" from true to false or vice-versa.
                }
            }

            if pressed {  // Displays the Tower when "pressed" is true.
                Tower()  // Tower struct is provided below.
            }
        }
    }
}

And this is the Tower struct:

struct Tower: View {
    var body: some View {
        VStack {
            Text("Level 3").transition(.move(edge: .leading))
            Text("Level 2")
            Text("Level 1").transition(.move(edge: .trailing))
        }
    }
}

The transition I want to achieve is pretty straightforward - I want Level 3 to fly in from the left, Level 1 to fly in from the right, and Level 2 to just fade in and out. With this code however, Levels 1, 2 and 3, all just fade in and out together. The .move(edge: .trailing) transition seems to not work for some reason.

The catch is that I definitely want the Tower struct and the Test struct to be separate at all times.
(I don't want to copy-paste any of the code that's within the Tower struct inside of the Test struct)

If you can show me how I can make the upper and lower levels fly in from different sides please let me know (if you can provide a code sample as well it'll help a ton).

1 Answers

Transition is an engine to present/remove a view in/from view hierarchy (with animation if animation is specified). It is applied to view as a whole, directly, and is not passed-into view's subviews. So if you try to add view into view hierarchy that does not have own transition it just appears, immediately, if there is animation then by default fade-in/out transition is applied (again, to view as a whole).

But you want to transition view's internals from outside. So here is possible solution.

Tested with Xcode 11.4 / iOS 13.4 (you can play with animations by yourself)

demo

struct Test: View {
    @State private var pressed = false

    var body: some View {
        VStack {
            Button(pressed ? "Press me to hide tower" : "Press me to show tower") {
                self.pressed.toggle()
            }

            Tower(show: $pressed)
        }.animation(.easeInOut)
    }
}

struct Tower: View {
    @Binding var show: Bool
    var body: some View {
        VStack {
            if show {
                Text("Level 3").transition(.move(edge: .leading))
                Text("Level 2")
                Text("Level 1").transition(.move(edge: .trailing))
            }
        }
        .animation(.easeInOut)
    }
}
Related