swiftuiswiftui-picker

Picker not selecting the desired option


I am trying to setup a picker, simple. I am successfully fetching an array of projects from firebase and populating the picker with the names of the projects. The problem that I am having is that I need to get the project id when I click the list but it's not doing anything after I click the option that I want. I tried to run it in a simulator and also on my iPhone and nothing happens after I make the selection. I am pretty sure I am not updating the picker and thus I am not updating the variable with the selected project id. I tried using the .onChange on the picker but nothing happens.

import SwiftUI

struct NewProjectView: View {
    @ObservedObject var viewModel = ProjectViewModel()
    @ObservedObject var clientViewModel = ClientFeedViewModel()
    
    @Environment (\.dismiss) var dismiss
    @State var projectName: String = "s"
    
    var clientNameIsEmpty: Bool {
        if projectName.count < 3 {
            return true
        } else {
            return false
        }
    }
    
    var clients: [Client] {
        return clientViewModel.clients
    }
    
    @State var selectedClient: String = ""
    
    var body: some View {
        NavigationView {
            VStack {
                
                Picker("", selection: $selectedClient) {
                    ForEach(clients, id:\.self) {
                        Text($0.clientName)
                        //I need to exctract the project id so I can pass it on
                    }
                }
                .pickerStyle(.menu)
                
                CustomTextField(text: $projectName, placeholder: Text("Client Name"), imageName: "person.text.rectangle")
                    .padding()
                    .background(Color("JUMP_COLOR")
                        .opacity(0.75)
                    )
                    .cornerRadius(10)
                    .padding(.horizontal, 40)
                Text("Name must contain more than 3 characters")
                    .font(.system(.subheadline))
                    .foregroundColor(.gray.opacity(0.3))
                    .padding(.top, 30)
                    .toolbar {
                        ToolbarItem(placement: .navigationBarLeading, content: {
                            Button(action: {
                                dismiss()
                            }, label: {
                                Text("Cancel")
                            })
                        })
                        ToolbarItem(placement: .navigationBarTrailing , content: {
                            Button(action: {
                                viewModel.newProject(name: projectName)
                                
                                dismiss()
                            }, label: {
                                Text("Save")
                            })
                            .disabled(clientNameIsEmpty)
                        })
                    }
            }
        }
        .presentationDetents([.height(400)])
        //.presentationDetents([.medium])
        .presentationDragIndicator(.visible)
    }
}

struct NewProjectView_Previews: PreviewProvider {
    static var previews: some View {
        NewProjectView()
    }
}
 

Here is the picker populated with the foo data: picker


Solution

  • Your selection variable $selectedClient needs to have a type that matches the tagged value of each item in the picker.

    As you're not specifying an explicit .tag for your text, the ForEach creates an implicit one using what it's using for tracking its loop, which in this case looks like it's a Client.

    You can either change selectedClient to be a type of Client, or tag your displayed subview with the string value to populate selectedClient with, e.g.:

    ForEach(clients, id: \.self) { client in
      Text(client.clientName)
        .tag(client.clientID)
    }
    

    Also, if each client has a unique ID, you're better off using that as ForEach's identifier than \.self. You can either specify id: \.clientID, etc., to use a single attribute – or you can add Identifiable conformance to Client and make sure that it has an id value that is guaranteed to be unique.