swiftuiswiftui-listeditmode

Inserted record without option icon in a list can be selected in edit mode


I have a list to show some records. A weird issue will happen as follow steps:

--tap "insert" button for some times(eg. 3 times)

--swipe a record from right to left to delete it

--tap "edit" button to switch to edit mode

--tap "insert" button to insert a new record

The first 2 records both have a "option button"(a small hollow circle). BUT the last inserted record has no "option button". The list seems like to reuse the view of the deleted record in "non-edit" mode. All of the 3 records can be selected by click. I wonder how to make the inserted record to also have a "option button".

struct ListEditModeSubviewUpdateTest: View {
    @State private var data = [Int]()
    @State private var editMode = EditMode.inactive
    @State private var selects = Set<Int>()
    @State private var base = 0
    
    var body: some View{
        VStack{
            Button {
                if editMode.isEditing{
                    editMode = .inactive
                }else{
                    editMode = .active
                }
            } label: {
                Text(editMode.isEditing ? "Done" : "Edit")
            }
            
            Button {
                base += 1
                data.append(base)
            } label: {
                Text("Insert")
            }
            
            List(selection: $selects){
                ForEach(data, id:\.self){item in
                    Text(String(item))
                }
                .onDelete{
                    data.remove(atOffsets: $0)
                }
            }
        }
        .environment(\.editMode, $editMode)
    }
}

Solution

  • Here is possible workaround. Tested with Xcode 13.4 / iOS 15.5

    // explicit state to force refresh List that
    // will drop cached cells
    @State private var forceRefresh = false 
    
    var body: some View{
        VStack{
            Button {
                if editMode.isEditing{
                    editMode = .inactive
                }else{
                    editMode = .active
                }
    
                // [!!!] cannot use `editMode` directly because it is also
                // changed on swipe to delete
                forceRefresh.toggle()            // << here !!
            } label: {
                Text(editMode.isEditing ? "Done" : "Edit")
            }
    
            Button {
                base += 1
                data.append(base)
            } label: {
                Text("Insert")
            }
    
            List(selection: $selects){
                ForEach(data, id:\.self){item in
                    Text(String(item)).id(item)
                }
                .onDelete{
                    data.remove(atOffsets: $0)
                }
            }
            .id(forceRefresh)         // << here !!
        }
        .environment(\.editMode, $editMode)
    }
    

    Test module on GitHub