3

For example, this is what happening right now

struct ContentView: View {
    @State var titleLable = "This is basic text"
    @State var isTextAnimated: Bool = false
    var body: some View {
        VStack {
            Text(titleLable)
                .offset(y: isTextAnimated ? 300 : 0)
                .animation(.linear)
            Button {
                isTextAnimated.toggle()
                if isTextAnimated {
                    titleLable = "New text appeared"
                } else {
                    titleLable = "This is basic text"
                }
            } label: {
                Text("Press")
            }
        }
        .padding()
    }

The code above leads to this in Live Preview: click there

This happens if text doesn't change its value ( I need this behaviour with changing ): click there

4 Answers 4

2

One of the simplest way to achieve this animation is to embed two Text inside a ZStackand modify their opacity, and modify the ZStack's offset rather than the individual Texts. in this way both the offset and the change between two texts will get animated. here is my code:

struct HomeScreen: View {
    @State var isTextAnimated: Bool = false
    
    var body: some View {
        ZStack{
            Text("Hello")
                .opacity(isTextAnimated ? 1 : 0)
            Text("World")
                .opacity(isTextAnimated ? 0 : 1)
        }
        .offset(y: isTextAnimated ? 150 : 0)
        
        
        Button(action: {withAnimation{isTextAnimated.toggle()}}){
            Text("Press")
        }
        
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

✅ This gives a much smoother animation than my answer, using matchedGeometryEffect.
2

As an alternative to .matchedGeometryEffect to animate moving and changing value of Text view you can "rasterize" text using .drawingGroup() modifier for Text. This makes text behave like shape, therefore animating smoothly. Additionally it's not necessary to define separate with linked with .machtedGeometryEffect modifier which can be impossible in certain situation. For example when new string value and position is not known beforehand.

Example

struct TextAnimation: View {
    var titleLabel: String {
        if self.isTextAnimated {
            return "New text appeared"
        } else {
            return "This is basic text"
        }
    }
    
    @State var isTextAnimated: Bool = false
    var body: some View {
        VStack {
            Text(titleLabel)
                .drawingGroup()  // ⬅️ It makes text behave like shape.
                .offset(y: isTextAnimated ? 100 : 0)
                .animation(.easeInOut, value: self.isTextAnimated)
            
            Button {
                isTextAnimated.toggle()
            } label: {
                Text("Press")
            }
        }
        .padding()
    }
}

Text's value and position animating

More informations

Apple's documentation about .drawingGroup modifier

Comments

0

To animate the position and the content of the Text label, you can use matchedGeometryEffect, as follows:

struct ContentView: View {
    @State var isTextAnimated: Bool = false
    @Namespace var namespace
    
    var body: some View {
        VStack {
            if isTextAnimated {
                Text("New text appeared")
                    .matchedGeometryEffect(id: "title", in: namespace)
                    .offset(y: 300)
            } else {
                Text("This is basic text")
                    .matchedGeometryEffect(id: "title", in: namespace)
            }
            Button {
                withAnimation {
                    isTextAnimated.toggle()
                }
            } label: {
                Text("Press")
            }
        }
        .padding()
    }
}

Comments

0

edit: I forgot to animate the text change

struct AnimationsView: View {
    @State private var buttonWasToggled = false
    @Namespace private var titleAnimationNamespace

    var body: some View {
        VStack {
            if !buttonWasToggled {
                Text("This is some text")
                    .matchedGeometryEffect(id: "text", in: titleAnimationNamespace)
                    .transition(.opacity)
            } else {
                Text("Another text")
                    .matchedGeometryEffect(id: "text", in: titleAnimationNamespace)
                    .transition(.opacity)
                    .offset(y: 300)
            }

            Button("Press me") {
                withAnimation {
                    buttonWasToggled.toggle()
                }
            }
        }
    }
}

A good way to animate such change is to animate the offset value rather than toggle a boolean:

struct AnimationsView: View {
    @State private var title = "This is basic text"
    @State private var offset: CGFloat = 0

    var body: some View {
        VStack {
            Text("Some text")
                .offset(y: offset)

            Button("Press me") {
                withAnimation {
                    // If we already have an offset, jump back to the previous position
                    offset = offset == 0 ? 300 : 0
                }
            }
        }
    }
}

or by using a boolean value:

struct AnimationsView: View {
    @State private var title = "This is basic text"
    @State private var animated = false

    var body: some View {
        VStack {
            Text("Some text")
                .offset(y: animated ? 300 : 0)

            Button("Press me") {
                withAnimation {
                    animated.toggle()
                }
            }
        }
    }
}

Note the important withAnimation that indicates to SwiftUI that you want to animate the changes made in the block. You can find the documentation here

The .animation(...) is optional and used if you want to change the behavior of the animation, such as using a spring, changing the speed, adding a delay etc... If you don't specify one, SwiftUI will use a default value. In a similar fashion, if you don't want a view to animate, you can use add the .animation(nil) modifier to prevent SwiftUI from animating said view.

Both solutions provided result in the following behavior : https://i.sstatic.net/tlSw1.jpg

3 Comments

These solutions only animate the text offset. The OP requires the text string itself to also change
Yeah, the main problem is to make this animation also when text value is changing
My bad, I've updated my solution to use geometryEffect. Ashley Mills' solution is the correct one but I've updated my answer just in case people read it : )

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.