I would like to have something like List(selection: ) in LazyVStack.
The problem is that I don't know how to manage the content to split in each element that it contains.
What I've tried to do:
public struct LazyVStackSelectionable<SelectionValue, Content> : View where SelectionValue : Hashable, Content : View { let content: Content var selection: Binding<Set<SelectionValue>>? @Environment(\.editMode) var editMode public init(selection: Binding<Set<SelectionValue>>?, @ViewBuilder content: () -> Content) { self.content = content() self.selection = selection } public var body: some View { if self.editMode?.wrappedValue == EditMode.active { HStack { content //here I would like to have something like ForEach (content, id:\.self) Button(action: { //add the UUID to the list of selected item }) { Image(systemName: "checkmark.circle.fill") //Image(systemName: selection?.wrappedValue.contains(<#T##member: Hashable##Hashable#>) ? "checkmark.circle.fill" : "circle") } } } else { content } } } struct ListView: View { @State private var editMode: EditMode = .inactive @State private var selection = Set<UUID>() @State private var allElements: [MyElement] = [MyElement(id: UUID(), text: "one"), MyElement(id: UUID(), text: "two" ), MyElement(id: UUID(), text: "tree" ) ] var body: some View { NavigationView { VStack { Divider() Text("LazyVStack") .foregroundColor(.red) LazyVStack { ForEach(allElements, id: \.self) { element in //section data Text(element.text) } } Divider() Text("LazyVStackSelectionable") .foregroundColor(.red) LazyVStackSelectionable(selection: $selection) { ForEach(allElements, id: \.self) { element in //section data Text(element.text) } } Divider() } .environment(\.editMode, self.$editMode) .navigationBarTitle(Text("LIST"), displayMode: .inline) .navigationBarItems(//EDIT trailing: Group { HStack (spacing: 15) { self.editButton self.delInfoButton .contentShape(Rectangle()) } } ) } } //MARK: EDIT MODE private func deleteItems() { DispatchQueue.global(qos: .userInteractive).async { Thread.current.name = #function selection.forEach{ idToRemove in if let index = allElements.firstIndex(where: { $0.id == idToRemove }) { allElements.remove(at: index) } } } } private var editButton: some View { Button(action: { self.editMode.toggle() self.selection = Set<UUID>() }) { Text(self.editMode.title) } } private var delInfoButton: some View { if editMode == .inactive { return Button(action: {}) { Image(systemName: "square.and.arrow.up") } } else { return Button(action: deleteItems) { Image(systemName: "trash") } } } } struct ListView_Previews: PreviewProvider { static var previews: some View { ListView() } } edit = .inactive
edit = .active
UPDATE
with Asperi's solution, I lose the propriety of LazyVStack, all the rows are loaded also if not displayed (and is also not scrollable:
struct SampleRow: View { let number: Int var body: some View { Text("Sel Row \(number)") } init(_ number: Int) { print("Loading LazySampleRow row \(number)") self.number = number } } struct LazySampleRow: View { let number: Int var body: some View { Text("LVS element \(number)") } init(_ number: Int) { print("Loading LazyVStack row \(number)") self.number = number } } var aLotOfElements: [MyElement] { var temp: [MyElement] = [] for i in 1..<200 { temp.append(MyElement(id: UUID(), number: i)) } return temp } struct ContentView: View { @State private var editMode: EditMode = .inactive @State private var selection = Set<UUID>() @State private var allElements: [MyElement] = aLotOfElements//[MyElement(id: UUID(), number: 1)] var body: some View { NavigationView { HStack { VStack { Text("LazyVStack") .foregroundColor(.red) ScrollView { LazyVStack (alignment: .leading) { ForEach(allElements, id: \.self) { element in //section data LazySampleRow(element.number) } } } } Divider() VStack { LazyVStack (alignment: .leading) { Divider() Text("LazyVStackSelectionable") .foregroundColor(.red) LazyVStackSelectionable(allElements, selection: $selection) { element in SampleRow(element.number) } Divider() } } } .environment(\.editMode, self.$editMode) .navigationBarTitle(Text("LIST"), displayMode: .inline) .navigationBarItems(//EDIT trailing: Group { HStack (spacing: 15) { self.editButton self.delInfoButton .contentShape(Rectangle()) } } ) } } //MARK: EDIT MODE private func deleteItems() { DispatchQueue.global(qos: .userInteractive).async { Thread.current.name = #function selection.forEach{ idToRemove in if let index = allElements.firstIndex(where: { $0.id == idToRemove }) { allElements.remove(at: index) } } } } private var editButton: some View { Button(action: { self.editMode.toggle() self.selection = Set<UUID>() }) { Text(self.editMode.title) } } private var delInfoButton: some View { if editMode == .inactive { return Button(action: {}) { Image(systemName: "square.and.arrow.up") } } else { return Button(action: deleteItems) { Image(systemName: "trash") } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } extension EditMode { var title: String { self == .active ? NSLocalizedString("done", comment: "") : NSLocalizedString("edit", comment: "") } mutating func toggle() { self = self == .active ? .inactive : .active } } 



