SwiftUI: Edit Mode Controls Disappear on Moving List Item

437 Views Asked by At

Why do the edit mode controls (delete button, drag handle) disappear after moving a list item?

It happens for me in iOS 15.5 and iOS 15.6 both in the simulator and on a physical device. In iOS 16.0 beta 3 when tapping Edit the controls show and then immediately disappear (without moving a list item). I tried Xcode 13.4.1 and 14.0 beta 3. macOS 12.5, Intel processor. I have tried resetting the simulator.

It works fine without the NavigationView or with a regular EditButton().

import SwiftUI

struct ContentView: View {
    @Environment(\.editMode) var editMode
    @State private var items = [1, 2, 3]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items, id: \.self) {
                    Text("Item \($0)")
                }
                    .onDelete {_ in }
                    .onMove(perform: moveItems)
            }.toolbar {
                //EditButton()
                Button("Edit") {
                    editMode?.wrappedValue = .active
                }
            }
        }
    }
    
    private func moveItems(from offsets: IndexSet, to destination: Int) {
        items.move(fromOffsets: offsets, toOffset: destination)
    }
}
2

There are 2 best solutions below

0
SeanR On

I had a similar issue where after adding an item to a list and entering edit mode, the new list item did not show the move and delete controls.

I changed from:

.onDelete(perform: onDelete)
.onMove(perform: onMove)

to:

.onMove(perform: onMove)
.onDelete(perform: onDelete)

and it started working for me.

Not sure if the same will resolve your issue. This seems like it might be an Apple bug.

0
bill.westland On

There are some strange (to me) behaviors regarding how editMode and the SwiftUI environment works. Moving the editMode manipulation code to a separate View structure usually works for me.

You don't say why you don't want to use EditButton(), but changing your example code to the following seems to work as intended:

struct ContentView: View {
    @State private var items = [1, 2, 3]

    var body: some View {
        NavigationView {
            List {
                ForEach(items, id: \.self) {
                    Text("Item \($0)")
                }
                .onDelete {_ in }
                .onMove(perform: moveItems)
            }.toolbar {
                CustomEditButton()
            }
        }
    }

    private func moveItems(from offsets: IndexSet, to destination: Int) {
        items.move(fromOffsets: offsets, toOffset: destination)
    }
}

struct CustomEditButton: View {
    @Environment(\.editMode) var editMode
    var body: some View {
        Button(editMode?.wrappedValue.isEditing == true ? "Done" : "Edit") {
            withAnimation {
                editMode?.wrappedValue = editMode?.wrappedValue.isEditing == true ? .inactive : .active
            }
        }
    }
}