Home > Software design >  swift picker not selecting item
swift picker not selecting item

Time:11-17

I have a short array of items that I want to display in a segmented picker. I'm passing the selected item (0, by default). The picker displays, but no item is selected, and the picker is unresponsive to clicks (in the simulator). I have a very similar picker that uses percentage values, and it works correctly. I am guessing that the issue has to do with the closure that I'm passing to the ForEach loop, but I am unclear on what syntax I should be using, if that is in fact the issue.

The code is as follows:

@State private var originalUnit = 0
let sourceUnits = ["meters","kilometers","feet","yards","miles"]

var body: some View {
   NavigationView {
      Form {
         Section {
             Picker("Unit", selection $originalUnit) {
                 ForEach(sourceUnits, id: \.self {
                     Text($0)
                 }
             } .pickerStyle(.segmented)
         } header: {
             Text("Choose Unit")
         }
      }  .navigationTitle("MyApp")
    }
  }
}

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
      ContentView()
    }
}

Any insights on this would be appreciated. Thanks in advance!

CodePudding user response:

You have a type mismatch between your originalUnit (Int) and your sourceUnits (String). Your selection needs to match the type.

struct ContentView: View {
    @State private var originalUnit = "meters" //<-- Here
    let sourceUnits = ["meters","kilometers","feet","yards","miles"]
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker("Unit", selection: $originalUnit) {
                        ForEach(sourceUnits, id: \.self) {
                            Text($0)
                        }
                    } .pickerStyle(.segmented)
                } header: {
                    Text("Choose Unit")
                }
            }  .navigationTitle("MyApp")
        }
    }
}

If, for some reason, you really needed originalUnit to be an Int, you could use enumerated (normally not the most efficient method for large collections in a ForEach, but that'll be inconsequential for something this small) so that the id is the index and matches the type (Int) of originalUnit:

struct ContentView: View {
    @State private var originalUnit = 0
    let sourceUnits = ["meters","kilometers","feet","yards","miles"]
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker("Unit", selection: $originalUnit) {
                        ForEach(Array(sourceUnits.enumerated()), id: \.0) { //<-- .0 is the index (Int)
                            Text($0.1) //<-- .1 is the original item String
                        }
                    } .pickerStyle(.segmented)
                } header: {
                    Text("Choose Unit")
                }
            }  .navigationTitle("MyApp")
        }
    }
}

CodePudding user response:

You can set tag for your picker's row. Tag is any hashable type. Refer this code work with any type of object for selection

struct TestView: View {
    @StateObject var viewModel = TestViewModel()
    var body: some View {
        VStack {
            Text(viewModel.selectedItem.title)
            Picker("Select item", selection: $viewModel.selectedItem) {
                ForEach(viewModel.items) { makeRowForItem($0) }
            }
        }
    }
    
    @ViewBuilder
    func makeRowForItem(_ item: Item) -> some View {
        Text(item.title).tag(item)
    }
}

struct Item: Identifiable, Hashable {
    var id = UUID().uuidString
    var title = "Untitled"
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

class TestViewModel: ObservableObject {
    @Published var selectedItem: Item
    @Published var items: [Item]
    
    init() {
        let list = (1..<10).map { Item(title: "Untitled \($0)") }
        items = list
        selectedItem = list.first!
    }
}
  • Related