Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does one enable selections in SwiftUI's List

I am trying to create a simple multiple selection List with SwiftUI. I am unable to make it work.

List takes a second argument which is a SelectionManager, so I tried creating a concrete implementation of one. But, it never gets called and the rows never highlight.

import SwiftUI  var demoData = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]  struct SelectKeeper : SelectionManager{     var selections = Set<UUID>()      mutating func select(_ value: UUID) {         selections.insert(value)     }      mutating func deselect(_ value: UUID) {         selections.remove(value)     }      func isSelected(_ value: UUID) -> Bool {         return selections.contains(value)     }      typealias SelectionValue = UUID  }  struct SelectionDemo : View {     @State var selectKeeper = SelectKeeper()      var body: some View {         NavigationView {             List(demoData.identified(by: \.self)){ name in                 Text(name)             }                 .navigationBarTitle(Text("Selection Demo"))         }     } }  #if DEBUG struct SelectionDemo_Previews : PreviewProvider {     static var previews: some View {         SelectionDemo()     } } #endif 

Code runs fine but rows don't highlight and the SelectionManager code is never called.

like image 584
Rumbles Avatar asked Jun 21 '19 15:06

Rumbles


People also ask

How do I allow rows to select in a list?

To support single selection, first add an optional property of the same type you're using inside your list. For example, if you were using a list of integers you would have an optional Int . Once you have that, pass it to your list using its selection parameter, then make sure your list is in editing mode.

How do you select multiple items in Swift?

The only way to get multiple selection in SwiftUI right now is by using EditButton . However, that's not the only instance you might want to use multiple selection, and it would probably confuse users if you used EditButton multiple selection when you're not actually trying to edit anything.


1 Answers

Depending on what you want, there are two ways to do this:

If you want to do this in "Edit mode":

You must enable "Edit mode" on the list before a selection matters. From the interface for List:

    /// Creates an instance.     ///     /// - Parameter selection: A selection manager that identifies the selected row(s).     ///     /// - See Also: `View.selectionValue` which gives an identifier to the rows.     ///     /// - Note: On iOS and tvOS, you must explicitly put the `List` into Edit     /// Mode for the selection to apply.     @available(watchOS, unavailable)     public init(selection: Binding<Selection>?, content: () -> Content) 

You do that by adding an EditButton to your view somewhere. After that, you just need to bind a var for something that implements SelectionManager(you don't need to roll your own here :D)

var demoData = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]  struct SelectionDemo : View {     @State var selectKeeper = Set<String>()          var body: some View {         NavigationView {             List(demoData.identified(by: \.self), selection: $selectKeeper){ name in                 Text(name)             }             .navigationBarItems(trailing: EditButton())             .navigationBarTitle(Text("Selection Demo \(selectKeeper.count)"))         }     } } 

This approach looks like this: enter image description here

If you don't want to use "Edit mode":

At this point, we're going to have to roll our own. Note: this implementation has a bug which means that only the Text will cause a selection to occur. It is possible to do this with Button but because of the change in Beta 2 that removed borderlessButtonStyle() it looks goofy, and I haven't figured out a workaround yet.

struct Person: Identifiable, Hashable {     let id = UUID()     let name: String }  var demoData = [Person(name: "Phil Swanson"), Person(name: "Karen Gibbons"), Person(name: "Grant Kilman"), Person(name: "Wanda Green")]  struct SelectKeeper : SelectionManager{     var selections = Set<UUID>()          mutating func select(_ value: UUID) {         selections.insert(value)     }          mutating func deselect(_ value: UUID) {         selections.remove(value)     }          func isSelected(_ value: UUID) -> Bool {         return selections.contains(value)     }          typealias SelectionValue = UUID      }  struct SelectionDemo : View {     @State var selectKeeper = Set<UUID>()          var body: some View {         NavigationView {             List(demoData) { person in                 SelectableRow(person: person, selectedItems: self.$selectKeeper)             }             .navigationBarTitle(Text("Selection Demo \(selectKeeper.count)"))         }     } }  struct SelectableRow: View {     var person: Person          @Binding var selectedItems: Set<UUID>     var isSelected: Bool {         selectedItems.contains(person.id)     }          var body: some View {         GeometryReader { geo in             HStack {                 Text(self.person.name).frame(width: geo.size.width, height: geo.size.height, alignment: .leading)             }.background(self.isSelected ? Color.gray : Color.clear)             .tapAction {                 if self.isSelected {                     self.selectedItems.remove(self.person.id)                 } else {                     self.selectedItems.insert(self.person.id)                 }             }         }     } } 

enter image description here

like image 127
piebie Avatar answered Oct 10 '22 00:10

piebie