Home > Net >  SwiftUI List Multiple Selection Data Model
SwiftUI List Multiple Selection Data Model

Time:07-15

I have a list of data objects that the user can select from a list. It's a multiple selection type of list. Typically you add a bindable Set to the List view initializer to allow multiple selection.

Multiple selection list

As you can see from the image, my design requires individual rows to be selected by a button in each row. This means I can not specify a bindable Set parameter to the list for multiple selection, I need to do this manually.

This is my data object:

public struct OP1Principle: Codable, Identifiable{
    public var id: String
    public var title: String
    public var description: String
    public var imageName: String?

    public var isSelected: Bool = false
}

And this is how I'm creating my list view:

    struct OP1PrincipleListView: View {
        // The model feeds data to a published array.
        @ObservedObject var principlesModel = OP1PrinciplesModel()
        
        var selectedCount: Int{
            return principlesModel.principles.reduce(0) { partialResult, principle in
                partialResult   (principle.isSelected ? 1 : 0)
            }
        }
        
        var body: some View {
            VStack{
                Text("Count: \(selectedCount)")
                List($principlesModel.principles){ $principle in
                    // The principle row takes in a principle data object and a bindable bool property for the selection.
                    OP1PrincipleRow(principle: principle, isSelected: $principle.isSelected)
                        .listRowBackground(Color.clear)
                        .listRowSeparator(.hidden)
                }
                .listStyle(.plain)
            }   
        }
    }

I had to add a "isSelected" property to my OP1Principle data object to achieve this, which is not ideal.

Is there another way to achieve manual multiple selection, without having to add this extra property to my data object?

CodePudding user response:

If it is needed only in view, then we can have property to selection, like for List, but work with it directly.

Here is a sketch of idea (provided code is not enough to prepare working demo)

struct OP1PrincipleListView: View {
    // The model feeds data to a published array.
    @ObservedObject var principlesModel = OP1PrinciplesModel()

    @State private var selectedIDs = Set<String>()   // << here !!

    var body: some View {
        VStack{
            Text("Count: \(selectedIDs.count)")
            List($principlesModel.principles){ $principle in

                let selected = Binding<Bool>(         // << here !!
                   get: { selectedIDs.contains(principle.id) },
                   set: { 
                     if $0 {
                        selectedIDs.insert(principle.id) 
                     } else {
                        selectedIDs.remove(principle.id) 
                     }
                   }
                )
                
                OP1PrincipleRow(principle: principle, isSelected: selected) // << here !!
                    .listRowBackground(Color.clear)
                    .listRowSeparator(.hidden)
            }
            .listStyle(.plain)
        }   
    }
  • Related