Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI, selecting a row in a List programmatically

I want to set the selected row in a List programmatically.
In my example below via 2 Buttons.

struct ContentView: View {

  @State private var selection = 2

  var body: some View {
    VStack {
      List() {
      //List(selection: $selection)  {.  // does not compile
        Text("Line 0").tag(1)
        Text("Line 1").tag(1)
        Text("Line 2").tag(2)
        Text("Line 3").tag(3)
        Text("Line 4").tag(4)
        Text("Line 5").tag(5)
      }
      .listStyle(SidebarListStyle())
      Text("Selected Item :\(self.selection)")
      HStack {
        Button(action: {if (self.selection < 5 ) { self.selection += 1 }} ) {Text("⬇︎")}
        Button(action: {if (self.selection > 0 ) { self.selection -= 1 }} ) {Text("⬆︎")}
      }
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
  }
}

trying to make the List selectable like this:

List(selection: $selection)  

does not compile.
Compiler complains: Unable to infer complex closure return type; add explicit type to disambiguate

like image 665
mica Avatar asked May 16 '20 14:05

mica


People also ask

How do you get the content of each row in SwiftUI?

Previously, with UIKit, one had to use a UITableView and implement delegate and data source methods (e.g. didSelectRowAt) to provide row content and navigate between views when a row is selected. Since SwiftUI is declarative, the content of each row is provided at the time of declaring the List.

How to handle navigation between views in SwiftUI list?

Since SwiftUI is declarative, the content of each row is provided at the time of declaring the List. In addition, we can now use a NavigationLink to handle navigation between views.

What does the disclosure indicator arrow mean in SwiftUI list?

The disclosure indicator arrow on the right is provided by SwiftUI for this particular type of List. The label is stretched out to the full width of the row to give more room to hit the gesture recognizer. More on that next.

How to show The Emoji on the row selected from list?

We will add a struct called DetailsView that will be shown when a row on the List is selected. This view will show the emoji, its name, as well as a short description about the emoji. Our DetailsView will look like so:


3 Answers

Selection type must be optional. Find below fixed code.

struct TestListSelectionOnAction: View {

  @State private var selection: Int? = 2 // optional !!

  var body: some View {
    VStack {
      List(selection: $selection)  {
        Text("Line 0").tag(0)
        Text("Line 1").tag(1)
        Text("Line 2").tag(2)
        Text("Line 3").tag(3)
        Text("Line 4").tag(4)
        Text("Line 5").tag(5)
      }
      .listStyle(SidebarListStyle())
      Text("Selected Item :\(self.selection ?? -1)")
      HStack {
        Button(action: {
            if (self.selection! < 5 ) { self.selection! += 1 }} ) {Text("⬇︎")}
        Button(action: {
            if (self.selection! > 0 ) { self.selection! -= 1 }} ) {Text("⬆︎")}
      }
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
  }
}
like image 195
Asperi Avatar answered Oct 21 '22 09:10

Asperi


thanks to Introspect: https://github.com/siteline/SwiftUI-Introspect

handle iPad Sidebar unable to set default row & make selected style visible in initial:

Example:

struct TempData: Identifiable {
    var id: Int
    var title: String
}

struct TempView: View {
    // handle selection nil: when detail view pushs view, selection == nil
    @State private var keepSelection: Int = 2
    // bind list selected row
    @State private var selection: Int? = 2 // set default row

    private var dataArr: [TempData] = [TempData(id: 1, title: "one"),
                                   TempData(id: 2, title: "two"),
                                   TempData(id: 3, title: "three")]

    var body: some View {
        List(selection: $selection) {
            ForEach(dataArr.indices) { index in
                NavigationLink(destination: Text("\(dataArr[index].title)").onAppear {
                    keepSelection = (selection != nil ? selection! : keepSelection)
                }, tag: dataArr[index].id, selection: $selection) {
                    HStack {
                    Text("\(dataArr[index].title)")
                    }
                }
                .introspectTableViewCell(customize: { cell in
                    // import Introspect
                    // https://github.com/siteline/SwiftUI-Introspect
                    cell.selectionStyle = .none
                    cell.backgroundColor = .systemGroupedBackground
                })
                .tag(dataArr[index].id) // or use enum
                .listRowBackground(keepSelection == dataArr[index].id ? Color.accentColor.cornerRadius(10.0): Color(UIColor.systemGroupedBackground).cornerRadius(10))
            }

        }
    }
}
like image 42
user15437479 Avatar answered Oct 21 '22 10:10

user15437479


It won't compile because the tags are not unique. You are setting tag 1 twice. It can not identify the value, thats why List(selection: $selection) does not work either.

 List() {
      //List(selection: $selection)  {.  // should compile now
        Text("Line 0").tag(0)
        Text("Line 1").tag(1)
        Text("Line 2").tag(2)
        Text("Line 3").tag(3)
        Text("Line 4").tag(4)
        Text("Line 5").tag(5)
 }
like image 35
davidev Avatar answered Oct 21 '22 09:10

davidev