List Rows Can Be Moved While Not Editing

Hi, I am building a view containing a list of struct objects. I have implemented the onMove to allow the list rows to be moved together with the toolbar EditButton. This works but I have a problem as per title, even when the EditButton is not clicked, i.e., not in editing mode, I still can move the list rows by long pressing the rows. I have searched high and low for a solution but couldn't find one that works perfectly.

Below is the code snippet.

I am a new iOS dev and really appreciate if someone could help me.

Many thanks!

    @AppStorage("fruits") private var fruits = Fruits.fruits
    
    var body: some View {
        NavigationStack {
            VStack {
                List {
                    ForEach(fruits) { fruit in
                        NavigationLink(destination: FruitsView(title: fruit.title)) {
                            fruit.icon
                            Text(fruit.title)
                        }
                    }
                    .onMove { fruits.move(fromOffsets: $0, toOffset: $1) }
                }
                .environment(\.defaultMinListRowHeight, UIElementConstants.listCellHeight)
            }
            .toolbar { EditButton() }
            .navigationTitle("Fruits")
        }
    }
struct Fruits: Identifiable, Codable {
    var id: UUID
    var title: String
    var iconName: String
    
    var icon: Image {
        Image(systemName: self.iconName)
    }
    
    init(id: UUID = UUID(), title: String, iconName: String) {
        self.id = id
        self.title = title
        self.iconName = iconName
    }
}

extension Fruits { 
    static let fruits: [Fruits] = [
        Fruits(
            title: "Apple",
            iconName: "basket.fill"),
        Fruits(
            title: "Banana",
            iconName: "basket.fill"),
        Fruits(
            title: "Pineapple",
            iconName: "basket.fill")
    ]
}
Answered by Vision Pro Engineer in 793140022

@eugeneee good catch!

Sorry about that, apply this modifier to your NavigationLink:

.moveDisabled(editMode?.wrappedValue == EditMode.inactive)

You'll need to read the environment value with @Environment(\.editMode) private var editMode for this to work.

One other thing - there is a small known issue with editMode in which it won't work in a NavigationStack unless the structure of your app is the following:

NavigationStack {
    VStack {
          ContentView()
    }
}

where ContentView is

struct ContentView: View {
  @Environment(\.editMode) private var editMode
   var body: some View {
       /// List etc
   }
}

Hi @eugeneee ,

You can definitely make this an editing-only feature! Instead of using a List and a ForEach, just use a List and provide editActions: .move like this:

 List($fruits, editActions: .move) { $fruit in
          NavigationLink(fruit.title) {
             //your destination
    }
}

Make sure to use a binding to the data so that when the user moves the row, the position is updated.

Now, the move handles will only show up when someone presses "edit"

Best,

Sydney

Hi Sydney,

Thank you very much for your reply!

I have updated my code as per your advice, but it seems that the rows are still able to be moved even when the Edit button is not clicked. Below is my updated code for your reference:

Thanks

Best, Eugene

@AppStorage("fruits") private var fruits = Fruits.fruits
    
    var body: some View {
        NavigationStack {
            VStack {
                List($fruits, editActions: .move) { $fruit in
                    NavigationLink {
                        FruitView(title: fruit.title)
                    } label: {
                        fruit.icon
                        Text(fruit.title)
                    }

                }
                .environment(\.defaultMinListRowHeight, UIElementConstants.listCellHeight)
            }
            .toolbar { EditButton() }
            .navigationTitle("Fruits")
        }
    }

Instead of the List constructor above, I have also tried the same one from your code snippet as shown below:

List($fruits, editActions: .move) { $fruit in
          NavigationLink(fruit.title) {
             FruitView(title: fruit.title)
    }
}
  • The second code block from my original post is untouched.
Accepted Answer

@eugeneee good catch!

Sorry about that, apply this modifier to your NavigationLink:

.moveDisabled(editMode?.wrappedValue == EditMode.inactive)

You'll need to read the environment value with @Environment(\.editMode) private var editMode for this to work.

One other thing - there is a small known issue with editMode in which it won't work in a NavigationStack unless the structure of your app is the following:

NavigationStack {
    VStack {
          ContentView()
    }
}

where ContentView is

struct ContentView: View {
  @Environment(\.editMode) private var editMode
   var body: some View {
       /// List etc
   }
}

Hi, thank you very much for the solution!

List Rows Can Be Moved While Not Editing
 
 
Q