NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

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.

   

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?

   

@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.

   

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

   

@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.

   

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?

   

Hacking with Swift is sponsored by MadMachine

SPONSORED Want to try Swift on microcontrollers? MadMachine provides ways to interact with the physical world in a Swift way. Join us and have fun!

Get it now

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

Reply to this topic…

You need to create an account or log in to reply.

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.