SwiftUI: componente Picker

Qué es Picker

El componente Picker permite al usuario seleccionar un valor entre un listado de opciones definido. Es un componente equivalente a UIPickerView y UISegmentedControl de UIKit.

Aquí podéis consultar la documentación oficial

La creación del componente se puede realizar de varias formas:

struct ContentView: View {
    let items = ["Sevilla", "Cádiz", "Huelva", "Córdoba", "Málaga", "Granada", "Jaén", "Álmería"]
    @State private var selection: String = "Sevilla"
    
    var body: some View {
        Picker("Select one option", selection: $selection) {
            ForEach(items, id: .self) {
                Text("($0)")
                    .foregroundColor(.blue)
            }
        }
    }
}

Esteinit consta de los siguientes parámetros:

  • String: texto a mostrar para introducir al Picker. En este ejemplo no se verá, ya que se usa en casos específicos como cuando lo incluimos en un NavigationView → Form.
  • selection: variable de estado con la selección actual del Picker.
  • content: es el listado de opciones que se presentarán en el picker.

También disponemos de otro init que nos da el mismo resultado:

struct ContentView: View {
    let items = ["Sevilla", "Cádiz", "Huelva", "Córdoba", "Málaga", "Granada", "Jaén", "Álmería"]
    @State private var selection: String = "Sevilla"
    
    var body: some View {
        Picker(selection: $selection, label: Text("Select one option")) {
            ForEach(items, id: .self) {
                Text("($0)")
                    .foregroundColor(.blue)
            }
        }
    }
}

En este caso tenemos los siguientes parámetros:

  • selection: variable de estado con la selección actual del Picker.
  • labelView a mostrar para introducir al Picker. En este ejemplo no se verá, ya que se usa en casos específicos como cuando lo incluimos en un NavigationView → Form.
  • content: es el listado de opciones que se presentarán en el picker.

Nota: el parámetro selection es una variable de estado que usará el componente Picker para asignar el valor seleccionado. Cuando queremos usar un valor fijo (por ejemplo, por motivos de pruebas) podemos usar la función .constant() que pertenece al struct Binding para cumplir con su implementación sin tener que declarar la variable de estado.

Cómo cargar y leer datos de un Picker

Para poder saber qué opción es la que está seleccionada en un Picker tenemos que crear una relación entre los datos cargados y la variable selection.

La carga de datos de un Picker se realizará normalmente a través del componente ForEach, iterando sobre un listado de elementos. En este punto lo importante es que cada elemento del ForEach sea único porque implementa el protocolo Identifiable o porque lo especificamos explicitamente en el ForEach (cómo cuando cargamos un listado de String e indicamos id: .self).

Al realizar la carga de esta forma el Picker es capaz de identificar cada elemento de forma única y lo que hace es volcar la selección actual en la variable selection que recibe en su init. Por este motivo, la variable selection deberá ser del mismo tipo que el id que usamos en el ForEach. Para una lista de String la variable de estado será String y tiene que tener uno de los valores preseleccionados (no puede ser opcional ?).

let items = ["Sevilla", "Cádiz", "Huelva", "Córdoba", "Málaga", "Granada", "Jaén", "Álmería"]
@State private var selection: String = "Sevilla"

En los ejemplos anteriores esta variable es de tipo String porque el id del ForEach es la misma variable sobre la que se itera (un String), pero puede ser cualquier otro tipo:

struct ContentView: View {
    @State private var selection: Flavor = Flavor.chocolate
    
    var body: some View {
        VStack {
            Picker(selection: $selection, label: Text("Select one option")) {
                ForEach(Flavor.allCases, id: .self) {
                    Text("($0.rawValue)")
                        .foregroundColor(.orange)
                }
            }
            Text("Selection: (selection.rawValue)")
        }
    }
    
    enum Flavor: String, CaseIterable, Identifiable {
        case chocolate = "Chocolate"
        case vanilla = "Vanilla"
        case strawberry = "Strawberry"
        
        var id: String { self.rawValue }
    }

Modificadores comunes para Picker

El componente Picker comparte los mismos métodos de personalización que el componente View, y pueden ser consultados en el siguiente enlace.

pickerStyle

Permite alternar entre diferentes modos de presentación del Picker. El parámetro debe implementar el protocolo PickerStyle.

struct ContentView: View {
    @State private var selection: Flavor = Flavor.chocolate

    var body: some View {
        ScrollView {
            VStack {
                Group {
                    Picker("Select one option", selection: $selection1) {
                        ForEach(Flavor.allCases, id: .self) {
                            Text("($0.rawValue)")
                                .foregroundColor(.blue)
                        }
                    }
                    .pickerStyle(WheelPickerStyle())
                    Text("Selection: (selection1.rawValue)")
                }
            }
        }
        .padding()
    }
    
    enum Flavor: String, CaseIterable, Identifiable {
        case chocolate = "Chocolate"
        case vanilla = "Vanilla"
        case strawberry = "Strawberry"

        var id: String { self.rawValue }
    }
}

Para iOS existen diferentes implementaciones:

WheelPickerStyle

Este tipo es equivalente a UIPickerView de UIKit. Es el tipo por defecto.

.pickerStyle(WheelPickerStyle())

SegmentedPickerStyle

Este tipo es equivalente a UISegmentedControl de UIKit.

.pickerStyle(SegmentedPickerStyle())

MenuPickerStyle (iOS 14)

.pickerStyle(MenuPickerStyle())

InlinePickerStyle (iOS 14)

.pickerStyle(InlinePickerStyle())

Ejemplo

Puedes encontrar este ejemplo en github.com bajo el apartado Picker.

Rafael Fernández,
iOS Tech Lider