EditMode Example not working

I tried the example from https://developer.apple.com/documentation/swiftui/editmode. It's not working for me.

struct ContentView: View {
    @Environment(\.editMode)
    private var editMode

    @State
    private var name = "Maria Ruiz"

    var body: some View {
        NavigationView {
            Form {
                Text(String(editMode!.wrappedValue.isEditing))

                if editMode?.wrappedValue.isEditing == true {
                    TextField("Name", text: $name)
                } else {
                    Text("test")
                }
            }
            .animation(nil, value: editMode?.wrappedValue)
            .toolbar { // Assumes embedding this view in a NavigationView.
                EditButton()
            }
        }
    }
}

It shows the texts "false" and "test", before and after clicking Edit. What am I missing? I'm using XCode 14.0.1 and the deployment target is iOS 16. I also tried on a real iPhone and on iOS 15.5 in the Simulator. Thanks for any help.

Answered by BabyJ in 730727022

I've found that editMode works very strangely and only some of the time when you know how it works.

Try extracting the parts that access the editMode property from the container that changes based on it, like List/Form.

// container that does editing
// changes based on editMode
Form {
    EditingView() // extract to new view
}


// EditingView
@Environment(\.editMode) private var editMode

if editMode?.wrappedValue.isEditing == true {
    Text("Editing")
} else {
    Text("Not Editing")
}
Accepted Answer

I've found that editMode works very strangely and only some of the time when you know how it works.

Try extracting the parts that access the editMode property from the container that changes based on it, like List/Form.

// container that does editing
// changes based on editMode
Form {
    EditingView() // extract to new view
}


// EditingView
@Environment(\.editMode) private var editMode

if editMode?.wrappedValue.isEditing == true {
    Text("Editing")
} else {
    Text("Not Editing")
}

In case anyone comes across this looking for another way, this will also work:

struct ContentView: View {
    @State private var numbers = ["One", "Two", "Three"]
    @State private var editMode: EditMode = EditMode.inactive
    
    var body: some View {
        NavigationStack {
            
            List {
                ForEach(numbers, id: \.self) { num in
                    Text(num)
                }
                .onDelete(perform: { _ in  })
                .onMove(perform: { _, _ in  })
            }
            
            .toolbar {
                Button {
                    if editMode == .inactive {
                        editMode = .active
                    } else {
                        editMode = .inactive
                    }
                } label: {
                    Text("edit")
                }
            }
            .environment(\.editMode, $editMode)
        }
    }
}

Create an edit mode variable and set it to active or inactive, and toggle this when you press the edit button in the toolbar. You still use the environment variable, just in a different way by passing the binding through the environment instead.

Neither of these solutions worked for me.

I needed a ForEach () with rows which can be deleted in EditMode and other views which show or hide depending on Edit Mode.

Without any solution, the Edit mode button allowed my ForEach list items to see Edit mode, but not my custom views. The environment variable never changed.

The first solution didn't work due to Bindings, key paths and other arguments needing to be passed through to lower levels which just got too complicated.

The second solution allowed my custom views to see the changes from the edit button, but the ForEach loop was not able to see the editMode anymore.

I came up with the following simple solution which didn't disrupt the ForEach loop, and enabled my CustomViews to react.

struct IfEditMode<Content: View>: View {
    var showView: () -> Content
    var notEditingView: (() -> Content)?

    init(@ViewBuilder show: @escaping () -> Content, @ViewBuilder `else`: @escaping () -> Content) {
        self.showView = show
        self.notEditingView = `else`
    }

    init(@ViewBuilder show: @escaping () -> Content) {
        self.showView = show
        self.notEditingView = nil
    }

    @Environment(\.editMode) var editMode

    var body: some View {
        if editMode?.wrappedValue.isEditing == true {
            showView()
        } else if let notEditingView = notEditingView {
            notEditingView()
        }
    }
}

Use it as follows:

// With a single argument, providing a View to show only in EditMode
IfEditMode {
    Text("In Edit Mode")
}
// Or a second argument, providing a View to show when not in EditMode
IfEditMode {
    Text("In Edit Mode")
} else: {
    Text("Read Only !")
}

Hope this helps

EditMode Example not working
 
 
Q