Reputation: 295
I'm having a hard time figuring out this animation thing. The problem is, the .onAppear wobbly animation (which should be forever looped) stops and freezes as soon as the view is dragged (which has another animation so that it smoothly moves towards the drag location).
Is there a way to keep the onAppear animation running independently of the view being dragged around? Important to note is that I do need the position to be dependent on the array (hence drag gesture writes directly to data), and I don't need the wobble (size animation) to be tied to the array at all.
Illustration of the animation freezing on drag (undesirable - would like to circles to still wobble during and after being dragged)
Please see code:
var body: some View {
Circle()
// .resizable()
.frame(width:size.width, height: size.height) //self.size is a state var
.foregroundColor(color)
.position(pos) //pos is a normal var bound to an array row
.gesture(DragGesture()
.onChanged { gesture in
withAnimation(.default) {
ballStorage.balls[numberInArray].position = gesture.location
}
}
.onEnded { gesture in
ballStorage.checkOverlaps(for: numberInArray)
}
) .onAppear {
withAnimation(Animation.easeInOut(duration: 1).repeatForever(autoreverses: true)) {
size = CGSize(width: size.width+20, height: size.height+20)
}
}
}
}
Upvotes: 1
Views: 292
Reputation: 257693
You need to separate circle into own view with own animation, then internal animation will be independent of external animation.
Here is a demo of solution on somehow replicated code. Prepared with Xcode 12.1 / iOS 14.1.
struct Ball: Identifiable, Hashable {
let id = UUID()
var position = CGPoint.zero
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
struct BallView: View {
let ball: Ball
var color = Color.blue
@State private var size = CGSize(width: 40, height: 40)
var body: some View {
Circle()
.frame(width:size.width, height: size.height) //self.size is a state var
.animation(Animation.easeInOut(duration: 1).repeatForever(autoreverses: true), value: size)
.foregroundColor(color)
.onAppear {
size = CGSize(width: size.width+20, height: size.height+20)
}
}
}
struct ContentView: View {
@State private var balls = [Ball(position: CGPoint(x: 100, y: 100)), Ball(position: CGPoint(x: 100, y: 200))]
@State private var off = CGSize.zero
@State private var selected = -1
var body: some View {
ZStack {
ForEach (Array(balls.enumerated()), id: \.1) { i, ball in
BallView(ball: ball)
.offset(x: i == selected ? off.width : 0, y: i == selected ? off.height : 0)
.position(ball.position)
.gesture(DragGesture()
.onChanged { gesture in
self.selected = i
withAnimation(.default) {
self.off = gesture.translation
}
}
.onEnded { gesture in
let pt = balls[i].position
self.balls[i].position = CGPoint(x: pt.x + gesture.translation.width, y: pt.y + gesture.translation.height)
self.off = .zero
self.selected = -1
// ballStorage.checkOverlaps(for: numberInArray)
}
)
}
}
}
}
Upvotes: 1