I have an AsyncImage that is zoomable via MagnifyGesture and drawable via a view modifier. Dragging and zooming works as expected. But now I want to show an overlay via onLongPressGesture, but this doesn't trigger. If I remove .draggable it works. How can I fix it?
public struct ContentView: View {
@ObservedObject private var viewState: ViewState
@State private var showAltText: Bool = false
@State private var currentZoom = 0.0
@State private var totalZoom = 1.0
public init(viewState vs:ViewState) {
viewState = vs
}
public var body: some View {
ZStack {
VStack {
if viewState.selectedComicStrip != nil {
AsyncImage(url: viewState.selectedComicStrip!.imageURL) { image in
image.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 400, maxHeight: 600)
} placeholder: {
Text("placeholder").frame(maxWidth: 400, maxHeight: 600)
}
.draggable()
.scaleEffect(currentZoom + totalZoom)
.gesture(
MagnifyGesture()
.onChanged { value in
currentZoom = value.magnification - 1
}
.onEnded { value in
totalZoom += currentZoom
currentZoom = 0
}
)
.onLongPressGesture {
showAltText.toggle()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
showAltText = false
}
}
} else {
Text("placeholder")
}
}
}.fullScreenCover(isPresented: $showAltText, content: {
ZStack {
Color.black.opacity(0.8)
.edgesIgnoringSafeArea(.all)
Text(viewState.selectedComicStrip!.altText)
.foregroundStyle(.white)
.padding()
}
})
.padding()
}
}
//https://stackoverflow.com/a/63082240/106435
struct DraggableView: ViewModifier {
@State var offset = CGPoint(x: 0, y: 0)
func body(content: Content) -> some View {
content
.gesture(DragGesture(minimumDistance: 0)
.onChanged { value in
self.offset.x += value.location.x - value.startLocation.x
self.offset.y += value.location.y - value.startLocation.y
})
.offset(x: offset.x, y: offset.y)
}
}
extension View {
func draggable() -> some View {
return modifier(DraggableView())
}
}
According to this Hacking with Swift post and a few others, SwiftUI will only trigger one gesture at a time by default. However, as far as I can tell from the docs, the exact behavior is undefined in general:
Inspired by this SO question, you could try a couple things:
simultaneousGesturemodifier. (Probably the most robust approach, suggested by the docs.)minimumDistanceon theDragGesture. (That way, it doesn't trigger immediately and preclude the long-press gesture from triggering too.)onLongPressGesturebefore theDragGesture. (Probably the most fragile option.)