En el vasto universo del desarrollo de interfaces de usuario, la capacidad de permitir al usuario elegir una opción entre un conjunto de posibilidades es fundamental. Ya sea seleccionando una talla de camiseta, un método de pago o un filtro de búsqueda, los controles de selección son omnipresentes.
En el desarrollo nativo con Apple, SwiftUI nos ofrece una herramienta camaleónica y potente para este propósito: el Picker.
A diferencia de los viejos tiempos de UIKit y AppKit, donde tenías que luchar con UIPickerView, UISegmentedControl o NSPopUpButton como entidades separadas, SwiftUI unifica todo bajo un único concepto declarativo. Este artículo es una inmersión profunda que te llevará desde la sintaxis básica hasta patrones avanzados, enseñándote a dominar el Picker en todas las plataformas de Apple.
Parte 1: Anatomía de un Picker
Antes de lanzarnos a crear interfaces complejas, debemos entender qué es estructuralmente un Picker. En términos de ciencias de la computación, un Picker es una representación visual de un tipo enumerado o una lista finita.
La Sintaxis Básica
Un Picker requiere tres ingredientes esenciales para funcionar:
- Un Título: Generalmente usado para accesibilidad o cuando el estilo lo requiere.
- Una Fuente de la Verdad (Binding): Una variable de estado que almacenará lo que el usuario seleccione.
- El Contenido: Las opciones disponibles.
Veamos el ejemplo más simple posible en Xcode:
import SwiftUI
struct BasicPickerView: View {
// 1. El Estado
@State private var selectedFlavor = "Vainilla"
let flavors = ["Chocolate", "Vainilla", "Fresa", "Menta"]
var body: some View {
VStack {
Text("Has elegido: \(selectedFlavor)")
// 2. El Componente
Picker("Sabor", selection: $selectedFlavor) {
ForEach(flavors, id: \.self) { flavor in
Text(flavor)
}
}
}
.padding()
}
}El Concepto Vital: Tags (Etiquetas)
En el ejemplo anterior, usamos ForEach con id: \.self. Esto funciona porque las cadenas de texto (Strings) son únicas y conformables a Hashable. Sin embargo, la magia real ocurre con el modificador .tag().
Cuando tocas una opción en el Picker, SwiftUI toma el valor del .tag() de esa vista y lo asigna a tu variable @State(selection). Si no especificas un tag explícito, SwiftUI intenta inferirlo (como en el caso del ForEach anterior), pero para tipos complejos, debes ser explícito.
Parte 2: El Camaleón del UI (PickerStyles)
La característica más potente del Picker en SwiftUI es su capacidad de adaptación. El mismo código base puede renderizarse de formas totalmente diferentes aplicando un simple modificador: .pickerStyle().
Esto es crucial para el diseño multiplataforma. No quieres un “rodillo” (wheel) gigante en una aplicación de escritorio de macOS, ni un menú desplegable pequeño en un Apple Watch.
1. El Estilo Automático (.automatic)
Por defecto, el Picker se adapta a su contenedor.
- En un
VStack: Suele verse como un menú o botón (iOS 15+). - En un
FormoList(iOS): Se comporta como una celda de navegación que abre una nueva pantalla con la lista de opciones.
2. SegmentedPickerStyle (.segmented)
Transforma el Picker en una barra horizontal de segmentos. Es ideal cuando tienes pocas opciones (entre 2 y 5). Es excelente para cambiar entre modos de vista (ej. “Mapa” vs “Lista”).
Picker("Modo", selection: $viewMode) {
Text("Mapa").tag(0)
Text("Lista").tag(1)
}
.pickerStyle(.segmented)3. WheelPickerStyle (.wheel)
El clásico rodillo de iOS. Útil para selección de fechas (aunque DatePicker es mejor para eso) o valores numéricos donde el espacio vertical no es un problema.
- Nota: En macOS, este estilo no es nativo de la misma manera y puede comportarse distinto o no estar disponible.
4. MenuPickerStyle (.menu)
Presenta un botón que, al ser pulsado, despliega un menú flotante o pop-up. Es el estándar moderno para ahorrar espacio en interfaces densas.
5. PalettePickerStyle (.palette)
Introducido recientemente, ideal para selección de colores o iconos, mostrando las opciones en una cuadrícula horizontal.
Parte 3: Desarrollando para iOS (iPhone y iPad)
En iOS, el contexto lo es todo. El comportamiento del Picker cambia drásticamente si está “suelto” en la vista o si está encapsulado en un Form.
El Patrón de Navegación en Formularios
Cuando colocas un Picker dentro de un Form en iOS, SwiftUI asume que estás creando una pantalla de configuración.
struct SettingsView: View {
@State private var notificationLevel = "Todas"
let levels = ["Todas", "Solo Importantes", "Ninguna"]
var body: some View {
NavigationStack {
Form {
Section(header: Text("Notificaciones")) {
Picker("Nivel de Alerta", selection: $notificationLevel) {
ForEach(levels, id: \.self) { level in
Text(level)
}
}
// Por defecto en Form, esto usa .navigationLink
}
}
.navigationTitle("Ajustes")
}
}
}¿Qué sucede aquí? En lugar de un menú desplegable, verás una fila con el título a la izquierda y el valor seleccionado a la derecha (en gris). Al tocar la fila, iOS navega (push) a una nueva pantalla generada automáticamente donde el usuario selecciona la opción. Al seleccionar, vuelve atrás automáticamente. Este es el comportamiento nativo de iOS que los usuarios esperan.
Si quieres evitar esto dentro de un Form y prefieres un menú inline o un rodillo, debes forzar el estilo: .pickerStyle(.wheel) o .pickerStyle(.inline).
Parte 4: Desarrollando para macOS
El desarrollo en macOS requiere una mentalidad diferente. Aquí tenemos un ratón/trackpad y ventanas redimensionables. Los controles deben ser más densos y precisos.
RadioGroupPickerStyle
Exclusivo de macOS. Muestra las opciones como botones de opción (radio buttons) donde solo uno puede estar activo.
Picker("Renderizado", selection: $renderQuality) {
Text("Bajo").tag(Quality.low)
Text("Medio").tag(Quality.medium)
Text("Alto").tag(Quality.high)
}
.pickerStyle(.radioGroup)PopUpButtonPickerStyle
Es el equivalente al NSPopUpButton. Es el estilo por defecto en macOS para la mayoría de los contenedores. Ahorra mucho espacio y es familiar para los usuarios de escritorio.
Consejo Pro para macOS: Asegúrate de que tus etiquetas (Text) sean breves. En macOS, los controles de interfaz a menudo tienen anchos fijos o compiten por el espacio horizontal en las barras de herramientas.
Parte 5: Desarrollando para watchOS
El Apple Watch presenta el mayor desafío: pantallas diminutas y dedos grandes.
La Corona Digital es tu Amiga
En watchOS, el estilo .wheel está altamente optimizado para usar la Digital Crown. Proporciona retroalimentación háptica mientras el usuario se desplaza por las opciones. Es la forma más ergonómica de seleccionar valores en el reloj.
Si tienes una lista de opciones de texto (como elegir una ciudad), el estilo por defecto (.automatic) presentará una lista vertical de botones grandes, fácil de tocar mientras caminas.
// En watchOS
Picker("Ciudad", selection: $city) {
Text("Madrid").tag("MAD")
Text("Barcelona").tag("BCN")
Text("Sevilla").tag("SVQ")
}
// Sin estilo especificado, crea una lista de navegación.Parte 6: Potenciando el Picker con Enums
Usar arrays de Strings (["Opción A", "Opción B"]) es propenso a errores. Si cometes un error tipográfico en una cadena, la selección fallará silenciosamente.
La forma profesional de usar Pickers en Swift es mediante Enums.
Paso 1: Definir el Enum
El enum debe conformar a String (para el valor crudo), CaseIterable (para poder iterar sobre él) e Identifiable (para usarlo en ForEach sin problemas).
enum CoffeeSize: String, CaseIterable, Identifiable {
case small = "Pequeño"
case medium = "Mediano"
case large = "Grande"
case venti = "Venti"
var id: Self { self } // Requisito de Identifiable
// Propiedad calculada para iconos (Opcional)
var iconName: String {
switch self {
case .small: return "cup.and.saucer"
case .medium: return "mug"
case .large: return "mug.fill"
case .venti: return "pail" // Broma interna ;)
}
}
}Paso 2: Implementar el Picker Tipado
struct CoffeeOrderView: View {
@State private var selectedSize: CoffeeSize = .medium
var body: some View {
VStack {
Picker("Tamaño", selection: $selectedSize) {
ForEach(CoffeeSize.allCases) { size in
// Aquí personalizamos la vista de cada fila
HStack {
Image(systemName: size.iconName)
Text(size.rawValue)
}
.tag(size) // CRUCIAL: El tag debe coincidir con el tipo de @State
}
}
.pickerStyle(.segmented)
}
.padding()
}
}Al usar Enums:
- Seguridad de Tipos: El compilador te avisa si olvidas un caso.
- Refactorización: Si cambias “Pequeño” a “Corto”, solo cambias el Enum, no toda la UI.
- Tags Automáticos: En muchos casos, SwiftUI infiere el tag si el tipo es el mismo, pero poner
.tag(size)explícitamente es una buena práctica defensiva.
Parte 7: Personalización Visual Avanzada
El contenido dentro del bloque del Picker no tiene por qué ser solo texto. SwiftUI es composicional.
Imágenes y Formas
Puedes poner Image, Circle, o cualquier vista compleja dentro de las opciones del Picker.
Advertencia: No todos los estilos soportan contenido complejo.
.segmented: En iOS 16+ soporta texto e imágenes, pero si pones vistas muy complejas, puede fallar o verse mal..wheel: Solo soporta texto plano consistentemente..menu: Soporta iconos y texto (Label).
Ejemplo de selector de color personalizado:
@State private var selectedColor = Color.red
let colors: [Color] = [.red, .blue, .green, .orange]
Picker("Color", selection: $selectedColor) {
ForEach(colors, id: \.self) { color in
HStack {
Circle().fill(color).frame(width: 20, height: 20)
Text(color.description)
}
.tag(color)
}
}Parte 8: Solución de Problemas Comunes (Troubleshooting)
A lo largo de mi experiencia con equipos de desarrollo, he visto que el 90% de los problemas con Pickers se reducen a dos causas:
1. El Misterio del Tag Perdido
Síntoma: Seleccionas una opción en la UI, pero la variable @State no cambia, o el Picker salta de nuevo a la selección original. Causa: El tipo de dato de tu variable @State no coincide exactamente con el tipo de dato del .tag(). Ejemplo: Tu estado es un Int (@State var index = 0), pero tus tags son String o no pusiste tags y el ForEach usa strings. Swift es estricto. Un Int nunca será igual a un String.
2. El Problema de la Selección Opcional
¿Qué pasa si quieres que el Picker empiece sin nada seleccionado? Debes usar un Binding opcional: @State private var selection: String? = nil. Sin embargo, el Picker estándar de SwiftUI no siempre maneja bien los nulos visualmente dependiendo del estilo. A menudo mostrará la primera opción o un espacio en blanco. Es mejor usar un valor “centinela” o “placeholder” en tu Enum, como case none = "Seleccionar...".
Parte 9: Rendimiento y Listas Grandes
Un error común es intentar usar un Picker para seleccionar entre miles de opciones (ej. una lista de todos los países del mundo o monedas).
Si cargas 200 opciones en un MenuPickerStyle, SwiftUI tiene que construir esas 200 vistas. Aunque es eficiente, puede causar un pequeño tirón (lag).
Para listas muy grandes, no uses un Picker estándar. En su lugar:
- Usa un
NavigationLinkque lleve a una vista personalizada con unListy un buscador (.searchable). - Al tocar un elemento de esa lista, actualizas el estado y cierras la vista (
dismiss). Esto ofrece una experiencia de usuario (UX) mucho mejor que un rodillo infinito o un menú que se sale de la pantalla.
Conclusión
El componente @Observable ha cambiado el juego de los datos, pero el Picker sigue siendo el rey de la interacción de selección.
Dominar el Picker en SwiftUI no se trata solo de saber escribir el código; se trata de entender cuándo usar qué estilo.
- ¿Estás en un formulario de iOS? Deja que el sistema use la navegación.
- ¿Son solo dos opciones? Usa
.segmented. - ¿Estás en el Watch? Piensa en la Corona Digital.
La belleza de SwiftUI radica en que puedes escribir la lógica de tu selección una vez (usando Enums y Estado) y luego simplemente cambiar el modificador .pickerStyle para adaptar tu aplicación a un iPhone, un Mac o un reloj.
Si tienes cualquier duda sobre este artículo, contacta conmigo y estaré encantado de ayudarte 🙂. Puedes contactar conmigo en mi perfil de X o en mi perfil de Instagram










