I'm starting out using DragGestures in SwiftUI. I started with a simple internet example, did a couple modifications, everything was working fine. Here's that code:
struct DragTest: View {
@State var viewState = CGSize.zero
var body: some View {
RoundedRectangle(cornerRadius: 30)
.fill(Color.blue)
.frame(width: 300, height: 400 + viewState.height)
.offset(x:0, y: viewState.height / 2)
.gesture(
DragGesture().onChanged { value in
viewState = value.translation
}
.onEnded { value in
withAnimation(.spring()) {
viewState = .zero
}
}
)
}
}
But if I change the frame height adjustment to subtract viewstate.height instead of add, the code will hang in preview or in the simulator with 100% processor usage. It seems like some sort of update loop.
struct DragTest: View {
@State var viewState = CGSize.zero
var body: some View {
RoundedRectangle(cornerRadius: 30)
.fill(Color.blue)
.frame(width: 300, height: 400 - viewState.height)
.offset(x:0, y: viewState.height / 2)
.gesture(
DragGesture().onChanged { value in
viewState = value.translation
}
.onEnded { value in
withAnimation(.spring()) {
viewState = .zero
}
}
)
}
}
What is going on here? If I don't understand the root cause, I'm not going to get very far with drag gestures.
I think I've sorted this out, though the symptoms on the way to solution were more than confusing. It appears that if you plan on modifying the geometry of an object (.offset
doesn't do this, but .frame
does, it seems), then you need to perform the DragGesture
in a different coordinate space which does not change size during the DragGesture
. (What coordinate space does DragGesture
normally operate in? - I don't know, and the documentation I found didn't say). Here is the code that works, plus I tried several variants.
struct DragTest3: View {
@State var trans = CGSize.zero
var body: some View {
Group {
RoundedRectangle(cornerRadius: 30)
.fill(Color.blue)
.frame(width: 300, height: 400 - trans.height)
.gesture(
DragGesture(coordinateSpace: .named("outer"))
.onChanged { value in
trans = value.translation
}
)
}
.frame(maxHeight: .infinity)
.coordinateSpace(name: "outer")
.border(.black)
}
}
Most simple DragGesture
examples on the internet only use the .offset
modifier, so they don't crash, I couldn't easily find less trivial examples. My theory is that if the coordinate space in which the DragGesture
is occurring is changing size, the code can enter a death spiral. It would be nice if the mechanisms were described in greater detail somewhere.