Nicolas Mandica
Nicolas Mandica

Reputation: 861

Create a transition whose removal depends on a @State value when the view is removed

I would like to create a transition whose removal depends on a @State value when the view is removed.

Here is what I have tried.

If isValid is true when the view appears, it will use .move(edge: .trailing) for removal, even if isValid becomes false in the meantime.

What I try to obtain is .move(edge: .leading) transition if isValid is false when the view is removed, even if it was true when the view was inserted.

The problem appears when I toggle show and isValid at the same time.

struct TextTransitionView: View {
    @State var isValid = false
    @State var show = false

    var body: some View {
        VStack {
            Spacer()

            if show {
                Text("TEXT").transition(slideTransition)
            }

            Spacer()

            Text("Move to \(isValid ? "right" : "left")")

            Button("Toggle isValid") {
                self.isValid.toggle()
            }

            Button("Toggle show") {
                withAnimation { self.show.toggle() }
            }

            Button("Toggle isValid and show") {
                withAnimation {
                    self.isValid.toggle()
                    self.show.toggle()
                }
            }
        }
    }

    var slideTransition: AnyTransition {
        let removal = isValid ? AnyTransition.move(edge: .trailing) : AnyTransition.move(edge: .leading)
        return .asymmetric(insertion: .identity, removal: removal)
    }
}

Upvotes: 2

Views: 143

Answers (3)

Nicolas Mandica
Nicolas Mandica

Reputation: 861

The problem was with isValid.toggle() being inside the withAnimation. Getting this line out of the withAnimation block solved the problem.

Button("Toggle isValid and show") {
    self.isValid.toggle()
    withAnimation {
        self.show.toggle()
    }
}

Upvotes: 0

Chris
Chris

Reputation: 8091

do you mean like so?

struct ContentView: View {

    @State var isValid = false

    var slideTransition: AnyTransition {
        let removal = isValid ? AnyTransition.move(edge: .trailing) : AnyTransition.move(edge: .leading)
        return .asymmetric(insertion: .identity, removal: removal)
    }

    var body: some View {
        VStack {
            Button(action: {
                withAnimation() {
                    self.isValid.toggle()
                }
            }) {
                Text("animate it")
            }
            if isValid {
                Text("text").transition(slideTransition)
            }
        }
    }
}

Upvotes: 0

Asperi
Asperi

Reputation: 257563

Here is possible approach. Tested with Xcode 11.4 / iOS 13.4

demo

struct DemoTextTransition: View {
    @State var isValid = false
    @State var show = false
    var body: some View {
        VStack {
            if show {
                Text("TEXT").transition(slideTransition)
            }
            Button("Valid: \(isValid ? "True" : "False")") {
                self.isValid.toggle()
            }
            Button("Test") {
                withAnimation { self.show.toggle() }
            }
        }
    }

    var slideTransition: AnyTransition {
        let removal = isValid ? AnyTransition.move(edge: .trailing) : AnyTransition.move(edge: .leading)
        return .asymmetric(insertion: .identity, removal: removal)
    }
}

Upvotes: 1

Related Questions