I want to achieve behavior similar to the Apple Podcast app on the queue screen in iOS. Specifically, I need to:
ContextMenu
from appearing on long press on the hamburger image using LongPressGesture
and a StateGesture
variable. Unfortunately, after implementing this solution, I can no longer drag rows by pressing the hamburger image using onMove.struct ContentView: View {
@State var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
@GestureState var isDraggingImageView = false
var body: some View {
List {
ForEach(items, id: \.self) { item in
HStack {
Text(item)
Spacer()
Image(systemName: "line.3.horizontal")
.gesture(
LongPressGesture()
.updating($isDraggingImageView, body: { value, state, _ in
state = value
})
)
}
.contextMenu(ContextMenu(menuItems: {
if isDraggingImageView {
EmptyView()
} else {
Text("Menu Item 1")
Text("Menu Item 2")
}
}))
}
.onMove(perform: move)
}
}
func move(from source: IndexSet, to destination: Int) {
items.move(fromOffsets: source, toOffset: destination)
}
}
How to make onMove
and LongPressGesture
to work together?
I found a solution: Instead of contextMenu
, I am just using Menu
that allows me to include the views I want for the long press. Menu
has a primaryAction
parameter which can be used as a simple row item tap. So dragging with hamburger image is working (no context menu after long press), single tapping in row is working, and long press on row shows a context menu.
Here is a working solution:
struct ContentView: View {
@State var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
var body: some View {
List {
ForEach(items, id: \.self) { item in
HStack {
// Long press gesture displays menu
Menu {
Text("Menu item 1")
Text("Menu item 2")
} label: {
HStack {
Text(item)
Spacer()
}
.frame(maxWidth: .infinity)
} primaryAction: {
// Tap gesture action
print("Primary action \(item)")
}
Image(systemName: "line.3.horizontal")
}
}
.onMove(perform: move)
}
}
func move(from source: IndexSet, to destination: Int) {
items.move(fromOffsets: source, toOffset: destination)
}
}