Reputation: 1720
I need to animate a CGAffineTransform with a rotationAngle but I don't know if is possible because CGAffineTransform is not conform to Animatable. Maby with "withAnimation", but I don't understand how to use this. I know I can use rotationEffect but for some raisons I must to use CGAffineTransform.
My code:
import SwiftUI
import CoreLocation
struct Arrow: View {
var locationManager = CLLocationManager()
@ObservedObject var location: LocationManager = LocationManager()
private var animationArrow: Animation {
Animation
.easeInOut(duration: 0.2)
}
var body: some View {
VStack {
Image("arrow")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 300, height: 300)
.transformEffect(
CGAffineTransform(translationX: -150, y: -150)
.concatenating(CGAffineTransform(rotationAngle: CGFloat(-location.heading.degreesToRadians)))
.concatenating(CGAffineTransform(translationX: 150, y: 150))
)
.animation(animationArrow)
Text(String(location.heading.degreesToRadians))
.font(.system(size: 20))
.fontWeight(.light)
.padding(.top, 15)
Text(String(location.coordinates[0]))
.font(.system(size: 20))
.fontWeight(.light)
.padding(.top, 15)
Text(String(location.coordinates[1]))
.font(.system(size: 20))
.fontWeight(.light)
.padding(.top, 15)
}
}
}
Upvotes: 3
Views: 1863
Reputation: 40539
If you cannot use any of the existing animatable effects, you need to teach SwiftUI how to animate your transform matrix. You do that with a GeometryEffect. It is a protocol that conforms both to Animatable and ViewModifier.
If you want a full explanation on how GeometryEffect works, check my post: https://swiftui-lab.com/swiftui-animations-part2/
Here's a working example:
struct ContentView: View {
@State private var animate = false
var body: some View {
Rectangle()
.frame(width: 50, height: 50)
.modifier(MyEffect(angle: animate ? .pi * 2 : 0))
.onTapGesture {
withAnimation(.easeInOut(duration: 2.0)) {
self.animate.toggle()
}
}
}
}
struct MyEffect: GeometryEffect {
var angle: CGFloat
var animatableData: CGFloat {
get { angle }
set { angle = newValue }
}
func effectValue(size: CGSize) -> ProjectionTransform {
return ProjectionTransform(CGAffineTransform(rotationAngle: angle))
}
}
Responding to your comments, you can use withAnimation with any variable, not only booleans. If the variable ends up affecting an animatable parameter, it will animate. Here's an example:
struct ContentView: View {
@State private var angle: CGFloat = 0
var body: some View {
VStack {
Rectangle()
.frame(width: 50, height: 50)
.modifier(MyEffect(angle: angle))
Button("Go to 0") {
withAnimation(.easeInOut(duration: 2.0)) {
self.angle = 0
}
}
Button("Go to 360") {
withAnimation(.easeInOut(duration: 2.0)) {
self.angle = .pi * 2
}
}
}
}
}
Upvotes: 6