Reputation: 6187
I'm working on a SwiftUI list that shows tapable and long-pressable full-width items, which are movable, and allow for detail navigation.
I've noticed that .onLongPressGesture
isn't detected when the list allows for moving of items, because the List switches to drag-moving the long-pressed item instead.
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
let data = Array(0..<20)
var body: some View {
NavigationStack {
List {
ForEach(data, id:\.self) { item in
NavigationLink(destination: EmptyView(), label: {
Rectangle().fill(.mint)
.onTapGesture { print("tapped", item) }
.onLongPressGesture{ print("longPressed", item)}
})
}.onMove(perform: moveItems)
}
}
}
func moveItems(from source: IndexSet, to destination: Int) { }
}
PlaygroundPage.current.setLiveView(ContentView())
I've experimented further and found that using simultaneous gesture via simultaneousGesture()
fixes the missing notification on long presses, but instead removes scrolling ability from the List.
import SwiftUI
import PlaygroundSupport
struct ContentViewSimultaneous: View {
let data = Array(0..<20)
var body: some View {
NavigationStack {
List {
ForEach(data, id:\.self) { item in
NavigationLink(destination: EmptyView(), label: {
Rectangle().fill(.blue)
.simultaneousGesture(TapGesture().onEnded { print("tapped", item) })
.simultaneousGesture(LongPressGesture().onEnded { _ in
print("longPressed", item) })
})
}.onMove(perform: moveItems)
}
}
}
func moveItems(from source: IndexSet, to destination: Int) { }
}
PlaygroundPage.current.setLiveView(ContentViewSimultaneous())
I'm now looking for a way to make this work and would appreciate any insights! I'm new to SwiftUI and might miss something important.
Upvotes: 0
Views: 361
Reputation: 216
I think I was able to get this working as you describe. It works with no issues on iOS 15, but there seems to be an animation bug in iOS 16 that causes the rearrange icon not to animate in for some/all List rows. Once you drag an item in edit mode, the icon will display.
struct ContentView: View {
@State private var editMode: EditMode = .inactive
@State var disableMove: Bool = true
var body: some View {
let data = Array(0..<20)
NavigationView {
List {
ForEach(data, id:\.self) { item in
NavigationLink(destination: EmptyView(), label: {
Rectangle().fill(.mint)
.onTapGesture { print("tapped", item) }
.onLongPressGesture{ print("longPressed", item)}
})
}
.onMove(perform: disableMove ? nil : moveItems)
}
.toolbar {
ToolbarItem {
Button {
withAnimation {
self.disableMove.toggle()
}
} label: {
Text(editMode == .active ? "Done" : "Edit")
}
}
}
.environment(\.editMode, $editMode)
}
.onChange(of: disableMove) { disableMove in
withAnimation {
self.editMode = disableMove ? .inactive : .active
}
}
.navigationViewStyle(.stack)
}
func moveItems(from source: IndexSet, to destination: Int) { }
}
Upvotes: 4
Reputation: 17695
Not sure if this helps
enum Status {
case notPressed
case pressed
case longPressed
}
struct ContentView: View {
@State private var status = Status.notPressed
var body: some View {
Rectangle()
.foregroundColor(color)
.simultaneousGesture(LongPressGesture().onEnded { _ in
print("longPressed")
status = .longPressed
})
.simultaneousGesture(TapGesture().onEnded { _ in
print("pressed")
status = .pressed
})
}
var color: Color {
switch status {
case .notPressed:
return .mint
case .pressed:
return .yellow
case .longPressed:
return .orange
}
}
}
Upvotes: 0