UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Picker not showing selected value

Forums > SwiftUI

I have a Picker view inside of a form... myRating is an Array of rating objects loaded from a database. Everything works great but once I select a value it doesn't show selected on the form, only blank. Any help would be aweseom, Thanks!!

Picker("Rating:", selection: $selectedRating)
{
    ForEach(sysArrays.myRating) { rating in
        if (rating.rate!.contains("A")){
            Text(rating.rate ?? "Error")
                .bold()
                .font(.headline)
                .foregroundColor(.green)
        }else if(rating.rate!.contains("B")){
            Text(rating.rate ?? "Error")
                .bold()
                .font(.headline)
                .foregroundColor(.yellow)
        }else{
            Text(rating.rate ?? "Error")
                .bold()
                .font(.headline)
                .foregroundColor(.red)
        }
    }
}

2      

Hi Jeff. I am not 100% sure but I think it is to do with the selected myRating are optionals. You could try to make an wrappedMyRatings array that has the reasoning in it to produce a non-optioal array and use that in the ForEach

2      

Gave that a try and got the same result unfortunatly...

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Just double checking a mistake I made when I made a picker.

Is selectedRating an Int? When you select the top item in the picker, selectedRating would be 0 (zero), not one.

2      

No, it is a string. It doesn't matter what value I choose from the picker, it doesn't show up.

2      

From Paul's wonderful list of helpful information.

struct ContentView: View {
   var colors = ["Red", "Green", "Blue", "Tartan"]
   @State private var selectedColor = 0

   var body: some View {
      VStack {
         Picker(selection: $selectedColor, label: Text("Please choose a color")) {
            ForEach(0 ..< colors.count) {
               Text(self.colors[$0])
            }
         }
         Text("You selected: \(colors[selectedColor])")
      }
   }
}

The picker wants $selectedRating to be a number, not the value of the array element (the actual rating). Paul's example has selectedColor as 0 to start and then shows the colors[0] if that one was selected in the picker.

2      

I'm sorry, yes my selectedRating is an Int... I'll try and rewrite it to use the index of the array.

@State private var selectedRating = 0

2      

Still no luck...

2      

OK, sorry I missed this earlier. It appears you are taking the sysArrays.myRating and not using it in order (if (rating.rate!.contains("A")). So later, when you try to access the array, the picker isn't pointing to the same spot. Below code will show you what I mean. So I would make a helper function that takes sysArrays.myRating and puts it in an order that will be displayed, and can be picked from.

(Found from StackOverflow... put it anywhere outside your ContentView)

extension View {
    func Print(_ vars: Any...) -> some View {
        for v in vars { print(v) }
        return EmptyView()
    }
}

Underneath your picker, put in these 2 items (temporarily)

Print ("\(sysArrays.myRating)") // needs a capital P for Print

Unless I am mistaken, that will show the array of items you're trying to pick from

Text("\(selectedRating)")

That should show the number of the array element selected. When you change the picker, the first Print should stay the same, the Text one should change each time you pick a new item on the picker.

2      

I put in that code and selectedRating never changes from 0

2      

What did it show in the console for sysArrays.myRating? If it isn't the picker order that you want, then there is the issue I believe. Try creating a function that accepts sysArrays.myRating and returns a new array with the items (in order) for the picker. Then the picker will return 0 if it is the top one, increase by 1 until the last item.

2      

It showed the proper array and in the proper order... Not sure what is going on.

2      

Obviously I don't have the data associated this this, but when I made a test app with some items, it worked.

Mine (works as I think you intend)

let output = ["red", "Banana", "Green", "Blue", "Purple", "Apple"]

var body: some View {
   VStack {
      Picker("Rating:", selection: $selectedRating) {
         ForEach(0 ..< output.count) {

            if (self.output[$0].contains("A")) {
               Text(self.output[$0])
                  .bold()
                     .font(.headline)
                     .foregroundColor(.green)
           }else if(self.output[$0].contains("B")){
              Text(self.output[$0])
                 .bold()
                    .font(.headline)
                    .foregroundColor(.yellow)
          }else{
             Text(self.output[$0])
                .bold()
                .font(.headline)
                .foregroundColor(.red)
          }
       }
    }
    Text("You selected \(output[selectedRating])")
  }
 }

Yours

Picker("Rating:", selection: $selectedRating) {
                ForEach(0 ..< sysArrays.myRating.count) {

                    if (self.sysArrays.myRating[$0].rate.contains("A")) {
                        Text(self.sysArrays.myRating[$0].rate)
                            .bold()
                            .font(.headline)
                            .foregroundColor(.green)
                    }else if(self.sysArrays.myRating[$0].contains("B")){
                        Text(self.sysArrays.myRating[$0].rate)
                            .bold()
                            .font(.headline)
                            .foregroundColor(.yellow)
                    }else{
                        Text(self.sysArrays.myRating[$0].rate)
                            .bold()
                            .font(.headline)
                            .foregroundColor(.red)
                    }
                }
            }
            Text("You selected \(output[selectedRating])")

2      

sysArrays.myRating is an array of Rating objects. For some reason I can't call .count on the array.

2      

i dont know if this will make a difference but is your Rating object conform to Identifiable? Try that and also change the ForEach to included id: .self

2      

It didn't work for me either untill I added .tag(_:) for Text. I've found examples on internet where others had the same problem (https://greypatterson.me/2019/12/swiftui-picker/, https://stackoverflow.com/questions/57305372/why-does-binding-to-the-picker-not-work-anymore-in-swiftui). Could you give this a try?

2      

In your ForEach, you need to make it conform to Identifiable or add an id as I have below, and add a tag of the same type as the value you want returned.

You can fix that with: ForEach(sysArrays.myRating, id:.self) { rating in if (rating.rate!.contains("A")){ Text(rating.rate ?? "Error") .bold() .font(.headline) .foregroundColor(.green) .tag(rating)

3      

Does this code do what you want it to do?

import SwiftUI

struct Rating: Identifiable, Hashable {
    var id = UUID()
    var rate: String?
}

struct ContentView: View {

    let myArray: [Rating] = [Rating(rate: "A"), Rating(rate: "B"), Rating(rate: "B"), Rating(rate: nil)]
    @State private var selectedRating: Rating? = Rating()

    var body: some View {
        NavigationView {
            Form {
                Picker("Rating", selection: $selectedRating) {
                    ForEach(myArray, id: \.self){ rating in
                        Text(rating.rate ?? "Not Set")
                            .bold()
                            .font(.headline)
                            .foregroundColor(ratingColor(rating: rating.rate ?? "Not Set"))
                            .tag(rating as Rating?)
                    }
                }

                Text("Selected rating is \(selectedRating?.rate ?? "Not Set")")
            }
        }
    }
}

func ratingColor(rating: String) -> Color {
    if rating.contains("A") {
        return .green
    } else if rating.contains("B") {
        return .yellow
    } else {
        return .red
    }
}

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.