1

On this SwiftUI picker, I need to differentiate among instrument selection tag: nil for toggling isShowingInstrumentsAdd, instrument to select one, and... what to do nothing at all?

Picker("* Instrumento", selection: $instrument, content: {

   Text("Seleccionar").tag( ??? ) // <- Here

   Label("Add or edit", systemImage: "plus").tag(nil as Instrument?)
   ForEach(instruments) { instrument in
     Text(instrument.name!).tag(instrument as Instrument?)
   }
})

.onChange(of: instrument) { value in
  if value == nil  {
    isShowingInstrumentsAdd.toggle()
  }
}

Updated. Let me explain what I'm looking for. With the above code, I get this:

Screenshoot

I'd like to present "Seleccionar" as default selection (no selection), "Add or edit" to present a new view, and ForEach selection to save selection its value. But, this way, I can't differentiate between the to first options. How could I accomplish this?

1
  • 1
    nil as Instrument? or Optional<Instrument>.none. But I don't understand why you have both a Label and a Text. Commented Oct 31, 2023 at 17:12

2 Answers 2

1

You can create your own enum to encode the picker options:

enum InstrumentOption: Hashable {
    case seleccionar
    case addOrEdit
    case instrument(Instrument)
}

Then assigning tags is very intuitive:

@State var instrumentOption: InstrumentOption = .seleccionar

// ...

Picker("* Instrumento", selection: $instrument) {
    
    Text("Seleccionar").tag(InstrumentOption.seleccionar)
    Divider()
    Label("Add or edit", systemImage: "plus").tag(InstrumentOption.addOrEdit)
    ForEach(instruments) { instrument in
        Text(instrument.name).tag(InstrumentOption.instrument(instrument))
    }
}

You can also easily detect the selection of "add or edit":

.onChange(of: instrumentOption) { value in
    if value == .addOrEdit {
        // toggle the state...

        // and perhaps set it back to seleccionar
        instrumentOption = .seleccionar
    }
}

That said, I would personally use a Menu for this instead. It makes sense for the Text "Seleccionar" to be unselectable, and the "Add or edit" button should actually be a Button.

To maintain the appearance of a Picker in a Form, you can do something similar to what I did in my answer here:

@State var instrument: Instrument?

// ...

LabeledContent("* Instrumento") {
    Menu {
        Text("Seleccionar")
        // when instrument != nil, perhaps you can
        // instead show a deselect button that sets instrument = nil
        Button("Add or edit", systemImage: "plus") {
            // toggle your state here...
        }
        Picker("* Instrumento", selection: $instrument) {
            ForEach(instruments) { instrument in
                Text(instrument.name).tag(instrument as Instrument?)
            }
        }
    } label: {
        HStack {
            Text(instrument?.name ?? "Seleccionar")
            Image(systemName: "chevron.up.chevron.down")
        }
        .tint(.secondary)
        .imageScale(.small)
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, @Sweeper. Although your first correction is good, your second option is event better: exactly what I was looking for. Great!
0

Use a collection of optional values

let optInstruments = [nil] + instruments
ForEach(optInstruments) { instrument in
    Text(instrument?.name ?? “None”).tag(instrument)
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.