How would it be possible to animate Text
or TextField
views from Swift UI?
By animation I mean, that when the text changes it will "count up".
For example given some label, how can an animation be created that when I set the labels text to "100" it goes up from 0 to 100. I know this was possible in UIKit using layers and CAAnimations, but using the .animation()
function in Swift UI and changing the text of a Text
or TextField
does not seem to do anything in terms of animation.
I've taken a look at Animatable
protocol and its related animatableData
property but it doesn't seem like Text
nor TextField
conform to this. I'm trying to create a label that counts up, so given some value, say a Double
the changes to that value would be tracked, either using @State
or @Binding
and then the Text
or TextField
would animate its content (the actual string text) from what the value was at to what it was set to.
Edit:
To make it clearer, I'd like to recreate a label that looks like this when animated:
To animate the change, we can add animation(_:value:) modifier. For the value parameter, we should pass the value of the state variable that alters text font weight.
SwiftUI has built-in support for animations with its animation() modifier. To use this modifier, place it after any other modifiers for your views, tell it what kind of animation you want, and also make sure you attach it to a particular value so the animation triggers only when that specific value changes.
If you want a SwiftUI view to start animating as soon as it appears, you should use the onAppear() modifier to attach an animation.
SwiftUI uses Core Animation for its rendering by default, which offers great performance out of the box. However, for complex rendering you might find your code starts to slow down – anything below 60 frames per second (FPS) is a problem, but really you ought to aim higher because many iOS devices now render at 120fps.
There is a pure way of animating text in SwiftUI. Here's an implementation of your progress indicator using the AnimatableModifier protocol in SwiftUI:
I've written an extensive article documenting the use of AnimatableModifier (and its bugs). It includes the progress indicator too. You can read it here: https://swiftui-lab.com/swiftui-animations-part3/
struct ContentView: View {
@State private var percent: CGFloat = 0
var body: some View {
VStack {
Spacer()
Color.clear.overlay(Indicator(pct: self.percent))
Spacer()
HStack(spacing: 10) {
MyButton(label: "0%", font: .headline) { withAnimation(.easeInOut(duration: 1.0)) { self.percent = 0 } }
MyButton(label: "27%", font: .headline) { withAnimation(.easeInOut(duration: 1.0)) { self.percent = 0.27 } }
MyButton(label: "100%", font: .headline) { withAnimation(.easeInOut(duration: 1.0)) { self.percent = 1.0 } }
}
}.navigationBarTitle("Example 10")
}
}
struct Indicator: View {
var pct: CGFloat
var body: some View {
return Circle()
.fill(LinearGradient(gradient: Gradient(colors: [.blue, .purple]), startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 150, height: 150)
.modifier(PercentageIndicator(pct: self.pct))
}
}
struct PercentageIndicator: AnimatableModifier {
var pct: CGFloat = 0
var animatableData: CGFloat {
get { pct }
set { pct = newValue }
}
func body(content: Content) -> some View {
content
.overlay(ArcShape(pct: pct).foregroundColor(.red))
.overlay(LabelView(pct: pct))
}
struct ArcShape: Shape {
let pct: CGFloat
func path(in rect: CGRect) -> Path {
var p = Path()
p.addArc(center: CGPoint(x: rect.width / 2.0, y:rect.height / 2.0),
radius: rect.height / 2.0 + 5.0,
startAngle: .degrees(0),
endAngle: .degrees(360.0 * Double(pct)), clockwise: false)
return p.strokedPath(.init(lineWidth: 10, dash: [6, 3], dashPhase: 10))
}
}
struct LabelView: View {
let pct: CGFloat
var body: some View {
Text("\(Int(pct * 100)) %")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.white)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With