Search code examples
swiftuiswiftui-environment

How to change two lists' editmode separately


I have 2 lists in a vstack. I want to use 2 editbuttons to change the editmode of the 2 lists separately.

I know the 2 editbuttons will be associated with the same environment value of the struct EditModeInTwoLists like this:

struct EditModeInTwoLists: View {
    var body: some View {
        VStack{
            EditButton()
            List{
                ForEach(1..<5){_ in
                    Text("List1")
                }
            }
            
            EditButton()
            List{
                ForEach(1..<5){_ in
                    Text("List2")
                }
            }
        }
    }
}

Therefore, I encapsulate the lists into 2 separate childviews. Because, IMO, the 2 childviews should have separate environment value. I change the code like this:

struct EditModeInTwoLists: View {
    var body: some View {
        VStack{
            EmbedList1()
            EmbedList2()
        }
    }
}

struct EmbedList1: View{
    var body: some View{
        VStack{
            EditButton()
            List{
                ForEach(1..<5){_ in
                    Text("List1")
                }
            }
        }
    }
}
struct EmbedList2: View{
    var body: some View{
        VStack{
            EditButton()
            List{
                ForEach(1..<5){_ in
                    Text("List2")
                }
            }
        }
    }
}

However, the 2 editbuttons also toggle synchronously. Do I misunderstand anything? Is there any way to toggle the 2 editbuttons separately?

EDIT:

@Asperi 's approach works for me. However, if I customize a button to toggle the wrappedValue of editmode, the issue appears again. I don't quite understand of the reason.

struct EmbedList1: View{
    @Environment(\.editMode) private var editMode
    
    var body: some View{
        VStack{
            Button {
                if editMode != nil{
                    editMode!.wrappedValue = editMode!.wrappedValue.isEditing ? .inactive : .active
                }
            } label: {
                Text(editMode?.wrappedValue.isEditing == true ? "Done" : "Edit")
            }
            
            List{
                ForEach(1..<5){_ in
                    Text("List1")
                }
            }
        }
    }
}

Solution

  • We don't know what exactly EditButton does inside. Actually standard API is for standard things.

    In such cases it is more appropriate to work with EditMode directly to have control over it.

    Here is a possible approach (tested with Xcode 13.4 / iOS 15.5)

    demo

    struct EmbedList1: View{
        @State var editMode: EditMode = .inactive  // << own state
        var body: some View{
            VStack{
                // own button to switch
                Button(editMode == .inactive ? "Edit" : "Done") {
                    editMode = editMode == .active ? .inactive : .active
                }
                List{
                    ForEach(1..<5){_ in
                        Text("List1")
                    }
                }
            }
            .environment(\.editMode, $editMode) // << own env
        }
    }
    
    // do same inside EmbedList2
    

    Test module on GitHub