Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI detect edit mode

Tags:

swift

swiftui

I've returned to iOS development after a while and I'm rebuilding my Objective-C app from scratch in SwiftUI.

One of the things I want to do is use the default Edit Mode to allow entries in a List (backed by Core Data on CloudKit) to switch between a NavigationLink to a detail view and an edit view.

The main approach seems to be to handle it through a if statement that detects edit mode. The Apple documentation provides the following snippet for this approach on this developer page: https://developer.apple.com/documentation/swiftui/editmode

@Environment(\.editMode) private var editMode
@State private var name = "Maria Ruiz"

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

However, this does not work (I've embedded the snippet in a NavigationView as assumed). Is this a bug in Xcode 13.4.1? iOS 15.5? Or am I doing something wrong?

Update1:

Based on Asperi's answer I came up with the following generic view to handle my situation:

import SwiftUI

struct EditableRow: View {
#if os(iOS)
    @Environment(\.editMode) private var editMode
#endif
    
    @State var rowView: AnyView
    @State var detailView: AnyView
    @State var editView: AnyView

    var body: some View {
        NavigationLink{
            if(editMode?.wrappedValue.isEditing == true){
                editView
            }
            else{
                detailView
            }
            
        }label: {
            rowView
        }
    }
}

struct EditableRow_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            VStack {
                EditButton()
                EditableRow(rowView: AnyView(Text("Row")), detailView: AnyView(Text("Detail")), editView: AnyView(Text("Edit")))
            }
        }
    }

The preview works as expected, but this works partially in my real app. When I implement this the NavigationLink works when not in Edit Mode, but doesn't do anything when in Edit Mode. I also tried putting the whole NavigationLink in the if statement but that had the same result. Any idea why this isn't working?

Update2:

Something happens when it's inside a List. When I change the preview to this is shows the behavior I'm getting:

struct EditableRow_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            List {
                EditableRow(rowView: AnyView(GroupRow(title: "Title", subTitle: "Subtitle", type: GroupType.personal)), detailView: AnyView(EntryList()), editView: AnyView(Text("Edit")))
            }
            .navigationBarItems(trailing:
                HStack{
#if os(iOS)
                    EditButton()
#endif
                }
            )
         }
    }
}

Update3:

Found this answer: SwiftUI - EditMode and PresentationMode Environment

This claims the default EditButton is broken, which seems to be true. Replacing the default button with a custom one works (be sure to add a withAnimation{} block to get all the behavior from the stock button. But it still doesn't work for my NavigationLink...

Update4:

Ok, tried passing an "isEditing" Bool to the above View, not to depend on the Environment variable being available. This works as long as the View (a ForEach within a List in my case) isn't in "Editing Mode" whatever happens at that point breaks any NavigationLink it seems.

Update5:

Basically my conclusion is that the default Edit Mode is meant to edit the "List Object" as a whole enabling moving and deleting of rows. In this mode Apple feels that editing the rows themselves isn't something you'd want to do. I can see this perspective. If, however, you still want to enable a NavigationLink from a row in Edit Mode, this answer should help: How to make SwiftUI NavigationLink work in edit mode?

Asperi's answer does cover why the detection doesn't work. I did find that Edit Mode detection does work better when setting the edit mode manually and not using the default EditButton, see the answer above for details.

like image 538
Deddiekoel Avatar asked Jun 23 '26 08:06

Deddiekoel


1 Answers

It is on same level so environment is not visible, because it is activated for sub-views.

A possible solution is to separate dependent part into standalone view, like

    Form {
        InternalView()
    }
    .toolbar {
        EditButton()
    }

Tested with Xcode 13.4 / iOS 15.5

demo

Test module on GitHub

like image 158
Asperi Avatar answered Jun 25 '26 02:06

Asperi