I am aiming to achieve a callback when an animation of offset change is finished. So, I found a workaround online which uses the AnimatableModifier
to check when the animatableData
equals the target value.
struct OffsetAnimation: AnimatableModifier{
typealias T = CGFloat
var animatableData: T{
get { value }
set {
value = newValue
print("animating \(value)")
if watchForCompletion && value == targetValue {
DispatchQueue.main.async { [self] in onCompletion() }
}
}
}
var watchForCompletion: Bool
var value: T
var targetValue: T
init(value: T, watchForCompletion: Bool, onCompletion: @escaping()->()){
self.targetValue = value
self.value = value
self.watchForCompletion = watchForCompletion
self.onCompletion = onCompletion
}
var onCompletion: () -> ()
func body(content: Content) -> some View {
return content.offset(x: 0, y: value).animation(nil)
}
}
struct DemoView: View {
@State var offsetY: CGFloat = .zero
var body: some View {
Rectangle().frame(width: 100, height: 100, alignment: .center)
.modifier(
OffsetAnimation(value: offsetY,
watchForCompletion: true,
onCompletion: {print("translation complete")}))
.onAppear{
withAnimation{ offsetY = 100 }
}
}
}
But it turns out AnimatableModifier
is now deprecated. And I cannot find an alternative to it.
I am aware that GeometryEffect
will work for this case of offset change, where you could use ProjectionTransform
to do the trick. But I am more concerned about the official recommendation to "use Animatable directly".
Seriously, the tutorials about the Animatable
protocol I can find online all use examples of the Shape
struct which implicitly implements the Animatable
protocol. And the following code I improvised with the Animatable
protocol doesn't even do the "animating".
struct RectView: View, Animatable{
typealias T = CGFloat
var animatableData: T{
get { value }
set {
value = newValue
print("animating \(value)")
}
}
var value: T
var body: some View{
Rectangle().frame(width: 100, height: 100, alignment: .center)
.offset(y:value).animation(nil)
}
}
struct DemoView: View{
@State var offsetY: CGFloat = .zero
var body: some View {
RectView(value: offsetY)
.onAppear{
withAnimation{ offsetY = 100 }
}
}
}
Thanks for your kind reading, and maybe oncoming answers!