SwiftUI, selecting a row in a List programmatically

Viewed 3145

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

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)
  }
}

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))
            }

        }
    }
}

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)
 }
Related