I'm trying to animate a custom shape in SwiftUI derived from data in an array of objects. I've created this sample to illustrate the problem I'm encountering trying to get this working.
In this simple example. After a red dot is dragged it offsets its X and Y with a spring animation. However the shape snaps to the new position instead of smoothly animating with the dot.
I've tried several versions on animatableData but still haven't been able to get the animation working. Hope someone with more knowledge of this can help! Thank you.
final class Item: Identifiable {
let id: String
var position: CGPoint
let radius: CGFloat = 10
var offset: CGSize {
CGSize(width: position.x-radius, height: position.y-radius)
}
init(id: String, position: CGPoint) {
self.id = id
self.position = position
}
}
final class Manager: ObservableObject {
var items = [
Item(id: "Item 1", position: .init(x: 50, y: 200)),
Item(id: "Item 2", position: .init(x: 200, y: 200)),
Item(id: "Item 3", position: .init(x: 200, y: 50))
]
}
struct CustomShape: Shape {
@ObservedObject var manager: Manager
var animatableData: [Item] {
get { manager.items }
set { manager.items = newValue }
}
func path(in rect: CGRect) -> Path {
Path { path in
for i in 0..<animatableData.count {
let item = animatableData[i]
if i == 0 {
path.move(to: item.position)
} else {
path.addLine(to: item.position)
}
}
}
}
}
struct ContentView: View {
@ObservedObject var manager = Manager()
var body: some View {
ZStack(alignment: .topLeading) {
CustomShape(manager: manager)
ForEach(manager.items) { item in
Circle().foregroundColor(.red)
.frame(width: item.radius*2, height: item.radius*2)
.offset(item.offset)
.gesture(
DragGesture()
.onChanged { gesture in
manager.objectWillChange.send()
item.position = gesture.location
}
.onEnded { gesture in
withAnimation(.spring()) {
manager.objectWillChange.send()
item.position = CGPoint(
x: item.position.x + 20,
y: item.position.y + 20
)
}
}
)
}
}
}
}
