iosswiftswiftuiswiftui-animation

How to animate substring in a Text?


Currently, I am using multiple Texts in a horizontal stackview, to achieve animation of substring.

enter image description here

As you can see in the above animation, the text

- conversation
- meeting
- lecture

are animated.

However, there shortcoming of such an approach.

Text size is not consistent among different Text block. The following Text block are having different text size.

- Transform
- conversation/ meeting/ lecture
- to Quick Note

Any idea how we can achieve, so that all text blocks have same text size so that they appear like 1 sentence?

Or, how we can make the text blocks having constant text size, but able to perform line wrapping to next line, so that they appear like 1 sentence?

Currently, this is the code snippet I am using.

import SwiftUI

struct ContentView: View {
    var array = ["lecture", "conversation", "meeting"]
    
    @State var currentIndex : Int = 0
    @State var firstString : String = ""

    var body: some View {

        VStack {
            HStack {
                Text("Transform")
                    .lineLimit(1)
                    .minimumScaleFactor(0.5)
                    .font(.title)
                
               Text(firstString)
                    .lineLimit(1)
                    .minimumScaleFactor(0.5)
                    .font(.title)
                    .transition(AnyTransition.opacity.animation(.easeInOut(duration:1.0)))
                    .background(.yellow)
                
                Text("to Quick Note")
                    .lineLimit(1)
                    .minimumScaleFactor(0.5)
                    .font(.title)
            }.padding()
        }
        .animation(.default)
        .onAppear {
            firstString = array[0]
            
            let timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in
                if currentIndex == array.count - 1 {
                    self.firstString = array[0]
                    currentIndex = 0
                }
                else {
                    self.firstString = array[currentIndex+1]
                    currentIndex += 1
                }
            }
        }
    }
}

#Preview {
    ContentView()
}

Solution

  • To keep it all on one line with a consistent font size, try these changes:

    Other suggestions:

    Here is an updated version of the example. This also illustrates how to use task(id:priority:_:) for the timed animation.

    @State var currentIndex : Int = 0
    //@State var firstString : String = ""
    
    VStack {
        HStack {
            Text("Transform")
    
            Text(array[currentIndex])
                .background(.yellow)
    
            Text("to Quick Note")
        }
        .lineLimit(1)
        .font(.title)
        .minimumScaleFactor(0.5)
        .scaledToFit()
        .padding()
    }
    .animation(.default, value: currentIndex)
    .task(id: currentIndex) {
        try? await Task.sleep(for: .seconds(2))
        if currentIndex == array.count - 1 {
            currentIndex = 0
        } else {
            currentIndex += 1
        }
    }
    

    Animation