1

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?

1 Answer 1

2

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)
        }   
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.