I'm developing an app, one view of which has the primary goal of allowing users to reorder a List of NavigationLinks, but which I would also like to allow navigation & a few other things. I want users to be able to:
At the moment, enabling EditMode (to allow reordering) disables navigation & swipe actions; I haven't been able to find a workaround that allows all 3 functions simultaneously. Is there a good way to do this?
Here's an example:
struct ReorderableListView: View {
var items: [X] // List of custom objects
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: CustomView(item)) { // winds up disabled by edit mode
Text(item.name)
}
.swipeActions(edge: .leading) { Button("Swipe") {print("swipe")} } // winds up disabled by edit mode
}
.onMove { from, to in
print("move \(from) to \(to)")
}
}
.environment(\.editMode, .constant(.active)) // always in edit mode for reordering
}
}
}
Update:
Based on the accepted answer, it looks like this behavior got updated in iOS 16: Now, if onMove is implemented, you can reorder with a long press even when not in edit mode, which allows these three actions to coexist. I've added custom drag-handles to make this behavior obvious to the user, but otherwise I'm going with exactly the solution given in the accepted answer, below.
I am not sure that the .environment is necessary in this case (I could be wrong). You can remove that piece.
Additionally, you should add an ID to each item in your foreach. This should ideally come from your model when you create new items (for example, your model can contain an ID variable = UUID()), but for the time being we can add it inline in your foreach.
I had to write some code on my end to get this up and running, so my solution is based on the code I spun up (very similar to yours, but missing your custom items object):
struct ReorderableListView: View {
var items: [X] // List of custom objects
var body: some View {
NavigationView {
List {
// added ID here
ForEach(items, id: \.self) { item in
NavigationLink(destination: CustomView(item)) { // winds up disabled by edit mode
Text(item.name)
}
.swipeActions(edge: .leading) { Button("Swipe") {print("swipe")} } // winds up disabled by edit mode
}
.onMove { from, to in
print("move \(from) to \(to)")
}
}
// REMOVED .environment(\.editMode, .constant(.active)) // always in edit mode for reordering
}
}
}
For reference, here is the code I wrote locally to fill in the gaps. This is what worked & what I implemented within your code:
struct ReorderableListView: View {
@State var items = [1, 2, 3] // List of custom objects
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
NavigationLink(destination: ContentView2()) { // winds up disabled by edit mode
Text("\(item)")
}
.swipeActions(edge: .leading) { Button("Swipe") {print("swipe")} } // winds up disabled by edit mode
}
.onMove { from, to in
print("move \(from) to \(to)")
}
}
// .environment(\.editMode, .constant(.active)) // always in edit mode for reordering
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With