0

This code would produce an animation that crossfades the values whenever I change self.myTextString:

// In some View's body method
Text(self.myTextString)
.transition(.opacity)
.animation(.easeInOut(duration: 0.6), value: self.myTextString)

What I'd like it to do is fade out the old value completely THEN begin fading in the newValue.

It didn't seem clear / obvious how to do this. Do I need to make a custom Transition? Any examples anywhere of somebody doing such a thing? (Let the outgoing do something and complete, then the incoming can start animating?)

2 Answers 2

2

If you know the delay, you can make an asymmetric transition and delay on of them to appear as you like:

Text(self.myTextString)
    .transition(
        .asymmetric(
            insertion: .opacity.animation(.easeIn.delay(0.6)),
            removal: .opacity.animation(.easeOut)
        )
    )
    .id(myTextString)
    .animation(.default, value: self.myTextString)

Demo

Note transition works for new objects (new identities)

otherwise, you need to break the transition into two separated state (like a loading state and a complete state) and trigger for each separately

Sign up to request clarification or add additional context in comments.

1 Comment

That's exactly what I was looking for! I didn't realize one can modify the transition with the animation property like that. Many thanks!
1

I don't think you can implement this as a single transition, you need to clear the text and then set the new value.

One way to do this is to use withAnimation with a completion callback:

struct ContentView: View {
    let part1 = "The quick brown fox"
    let part2 = "jumps over the lazy dog"
    @State private var myTextString = ""
    @State private var showPart1 = true

    var body: some View {
        VStack {
            Text(myTextString)
                .animation(.easeInOut(duration: 0.6), value: myTextString)
                .frame(height: 50)

            Picker("Selection", selection: $showPart1) {
                Text(part1).tag(true)
                Text(part2).tag(false)
            }
            .pickerStyle(.segmented)
            .fixedSize()
            .padding()
            .onChange(of: showPart1, initial: true) { oldVal, newVal in
                withAnimation {
                    myTextString = ""
                } completion: {
                    myTextString = newVal ? part1 : part2
                }
            }
        }
    }
}

Animation

Some notes:

  • A transition only happens when one view is replaced with another. If the same Text view is being used to display the changing text (in other words, only the content is changing), then in fact there is no transition happening, only an animated change. So a .transition modifier is not required.

  • Instead of a .transition modifier, the modifier .contentTransition might still be useful.

  • The Text has a fixed height in this example, because when it is cleared it takes less space than when it has a value. This change otherwise causes the Picker in the same VStack to shuffle a bit.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.