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

Day 56 - Challenge part 1, Picker misleadingly shows a default which is not actually a value

Forums > 100 Days of SwiftUI

@geoff  

Hi all, I'm adding validation to the Bookwork genre picker. (I'm using a similar approach to the Order validation in Cupcake Corner.) It's very simple, just checking for non-empty:

    func isValid() -> Bool {
        guard !title.isEmpty else { return false }
        guard !author.isEmpty else { return false }
        guard !genre.isEmpty else { return false }

        return true
    }

The problem is that the Picker indicates that Fantasy is selected even when I haven't touched it. If I open the Picker, it shows a checkmark next to Fantasy! But the binding back to the genre string is empty! It will only get set properly if I explicitly choose Fantasy or some other genre in the list.

How do I make the Picker reflect the fact that no value has been picked yet? The default behavior is super deceptive, if I were a user I would assume that Fantasy was selected by default and that I wouldn't have to do anything if I wanted to enter another Fantasy novel.

(Edit - sorry for the ginormous screenshot, I don't know how to tell this text editor to scale it down.

https://i.ibb.co/TB3gPGC/Simulator-Screen-Shot-i-Phone-14-Pro-2022-09-23-at-11-34-46.png

Other picker styles, like .inline and .segmented do not have this deceptive behavior. But they also do not match the style that I actually want, which is the default menu-like behavior.

2      

Geoff wonders about Picker UI default values:

How do I make the Picker reflect the fact that no value has been picked yet?
The default behavior is super deceptive, if I were a user I would assume that Fantasy was selected by default

Please don't show ALL your view code. But can you paste in the snips showing your @Statebinding for genre, and how it's initialized? Also include the Picker code for genre. We don't need to see all the code!

"Geoff" is the "G" hard like goose? or soft like Jeff?

2      

@geoff  

Hi Obelix, it's pronounced identically to "Jeff" :)

Variable declaration:

@State private var genre = ""

List of genres:

let genres = ["Fantasy", "Horror", "Kids", "Mystery", "Poetry", "Romance", "Thriller"]

Picker code:

                    Picker("Genre", selection: $genre) {
                        ForEach(genres, id: \.self) {
                            Text($0)
                        }
                    }

The code is identical to Paul's, having followed along from his tutorials. (I also opened up his project-11 from github to confirm the behavior is the same.) The differences are in the validation which is elsewhere in the code.

My question is how to tell Picker to not deceptively show "Fantasy" by default when it has not actually been picked.

2      

Yikes! I thought there'd be an easy-to-find typo.

Now I'm not sure, leading me to think about default Picker behavior. Found this thread in Apple's development forums about no selection and Pickers. But did not give it a try. Maybe this nudges you in the right direction?

See -> Picker Behavior

2      

@geoff  

Thanks Obelix, I realize now that the problem I have is with iOS 16 default Picker behavior. In the video, https://youtu.be/fC3WxO6Z2T4?t=749, Paul is running 15.x and there is no confusion. However in iOS 16 the new default style is like .menu which causes confusion at least for me. I just verified myself by installed the iOS 15 simulator (which got deleted for me when upgrading to XCode 14).

I have thought about creating an invalid value for the Picker to render and to handle it later during the validation step. This would work, but it is in my opinion is just a hack.

let genres = ["Choose one", "Fantasy", "Horror", "Kids", "Mystery", "Poetry", "Romance", "Thriller"]
    func isValid() -> Bool {
        // a bunch of other guards [snip]
        guard genre != "Choose one" else { return false }

        return true
    }

I hope Apple provides an update to Picker for iOS 16 which brings back the more intuitive default behavior in iOS 15.x.

2      

I published my app the same day that Apple launched iOS16, and all my pickers were broken, just for the reason geoff says. There was no documentation at all in betas for that change, and I had to reconstruct it quickly. Bada, very bad, Apple. I'm still trying to get the options I had with this new "menu" style, the most important being in fact default values, or adding new options to this pickers just from the picker itself. I'm trying this way:

Picker("Instrument", selection: $instrument, content: {
      Text("Add/Edit").tag(nil as Instrument?)
      Divider()
      ForEach(instruments, id: \.self) { instrument in
         Text(instrument.name!).tag(instrument as Instrument?)
      }
})
.onChange(of: instrument) { value in
      if value == nil {
          isShowingInstrumentsAdd = true
      }
}
...
.sheet(isPresented: $isShowingInstrumentsAdd) {
  InstrumentsAddView(
}

Any further ideas?

2      

I have just run into this issue and luckily found this thread.

My workaround was to set the var for genre to the value equal to the first value in genres.

@State private var genre = "Fantasy"

let genres = ["Fantasy", "Horror", "Kids", "Mystery", "Poetry", "Romance", "Thriller"]

now genre is never empty and always equals the first item unless changed.

What I could not do though was set genre to genre[0], kept getting an error about

"Cannot use instance member 'genres' within property initializer; property initializers run before 'self' is available"

Any help appreciated

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.