Programación en Swift y SwiftUI para iOS Developers

EditButton en SwiftUI

Cualquier iOS Developer que lleve un tiempo en este mundo recuerda los días de UITableView en UIKit. Configurar el modo de edición requería lidiar con delegados, fuentes de datos, métodos como commit editingStyle y gestionar manualmente el estado de la interfaz. Era potente, sí, pero también propenso a errores y verboso.

Con la llegada de la programación Swift moderna y los paradigmas declarativos, Apple cambió las reglas del juego. Hoy en día, habilitar la edición de listas es tan simple como usar un EditButton en SwiftUI.

Sin embargo, detrás de esa aparente simplicidad hay un sistema basado en el entorno (Environment) que a veces puede confundir incluso a desarrolladores experimentados. En este extenso tutorial, exploraremos a fondo qué es, cómo funciona bajo el capó, y cómo implementar el EditButton en tus proyectos de Swift, abarcando las particularidades de Xcode al desarrollar para iOS, macOS y watchOS.


1. ¿Qué es el EditButton en SwiftUI?

El EditButton en SwiftUI es una vista preconfigurada proporcionada por el framework de Apple que alterna automáticamente el estado de edición de la vista actual.

En términos simples, es ese botón clásico que suele aparecer en la esquina superior derecha de una barra de navegación. Al tocarlo, cambia su texto de “Edit” a “Done” (o “Editar” a “OK” dependiendo del idioma del dispositivo), y pone a las vistas compatibles dentro de su jerarquía (usualmente un componente List) en modo de edición.

Este modo de edición revela controles de interfaz ocultos, como:

  • Controles de eliminación: Los icónicos círculos rojos con un signo menos.
  • Controles de reordenamiento: Las tres líneas horizontales (hamburguesa) a la derecha de la celda.
  • Controles de selección múltiple: Círculos de verificación (checkboxes) para seleccionar varios elementos a la vez.

Pero el botón por sí solo no hace magia. Funciona en conjunto con los modificadores de la lista para transformar la experiencia del usuario.


2. Implementación Básica en iOS

Para un iOS Developer, la implementación más común de un EditButton en SwiftUI es dentro de una lista de elementos dinámicos. Vamos a construir una aplicación sencilla para gestionar un inventario.

Abre Xcode, crea un nuevo proyecto de SwiftUI y observa este código:

import SwiftUI

struct InventarioView: View {
    @State private var items = ["MacBook Pro", "iPhone 15", "Apple Watch Ultra", "AirPods Pro"]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items, id: \.self) { item in
                    Text(item)
                }
                .onDelete(perform: eliminarItem)
                .onMove(perform: moverItem)
            }
            .navigationTitle("Inventario")
            .toolbar {
                EditButton() // ¡Aquí está la magia!
            }
        }
    }
    
    // Función para eliminar elementos
    func eliminarItem(at offsets: IndexSet) {
        items.remove(atOffsets: offsets)
    }
    
    // Función para reordenar elementos
    func moverItem(from source: IndexSet, to destination: Int) {
        items.move(fromOffsets: source, toOffset: destination)
    }
}

Analizando el Código:

  1. ForEach dentro de List: Es vital entender que los modificadores .onDelete y .onMove se aplican al ForEach, no a la List directamente. Esto se debe a que podrías tener secciones estáticas en tu lista que no quieres que sean editables.
  2. EditButton(): Lo colocamos dentro del modificador .toolbar. SwiftUI es lo suficientemente inteligente como para posicionarlo en el lugar estándar del sistema operativo (arriba a la derecha en iOS).
  3. La Magia: Solo por incluir EditButton(), SwiftUI conecta automáticamente el botón con la lista. Al pulsarlo, aparecerán los controles de borrado y reordenamiento.

3. Bajo el Capó: El Environment y el EditMode

Para dominar la programación Swift en interfaces declarativas, no basta con copiar y pegar. Debes entender por qué funciona.

El EditButton en SwiftUI no tiene una referencia directa a la List. No se pasan variables entre ellos. En su lugar, el botón modifica una variable de entorno (Environment Value) llamada editMode.

El editMode es un Binding<EditMode> opcional que fluye hacia abajo en la jerarquía de vistas. Los valores posibles de EditMode son:

  • .inactive: El estado normal.
  • .active: La lista está en modo de edición.
  • .transient: Un estado temporal (raramente usado directamente).

3.1. Leer el Estado de Edición

A veces, como iOS Developer, necesitas saber si la vista está en modo de edición para ocultar o mostrar otros elementos (por ejemplo, ocultar un botón de “Añadir Nuevo” mientras se está editando). Puedes leer este entorno así:

import SwiftUI

struct ListaConEstadoView: View {
    @State private var items = ["Manzanas", "Peras", "Naranjas"]
    // Leemos el valor del entorno
    @Environment(\.editMode) private var editMode
    
    var body: some View {
        NavigationView {
            VStack {
                if editMode?.wrappedValue.isEditing == true {
                    Text("¡Estás en modo edición!")
                        .foregroundColor(.red)
                        .padding()
                }
                
                List {
                    ForEach(items, id: \.self) { item in
                        Text(item)
                    }
                    .onDelete { items.remove(atOffsets: $0) }
                }
            }
            .navigationTitle("Frutas")
            .toolbar {
                EditButton()
            }
        }
    }
}

3.2. Activar la Edición Sin el EditButton Pordefecto

Si el diseño de tu app requiere un botón personalizado (quizás un icono de un lápiz flotante en lugar del texto estándar en la barra de navegación), puedes inyectar y modificar el estado tú mismo.

Nota: Modificar el editMode manualmente puede ser un poco verboso en SwiftUI debido a que requiere inyectar un @State local en el .environment.

struct CustomEditView: View {
    @State private var items = ["Swift", "Kotlin", "Python"]
    @State private var isEditing: EditMode = .inactive // Nuestro estado local
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items, id: \.self) { item in
                    Text(item)
                }
                .onDelete { items.remove(atOffsets: $0) }
            }
            .navigationTitle("Lenguajes")
            // Sobrescribimos el entorno con nuestro estado local
            .environment(\.editMode, $isEditing)
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        withAnimation {
                            isEditing = isEditing == .active ? .inactive : .active
                        }
                    }) {
                        Image(systemName: isEditing == .active ? "checkmark.circle.fill" : "pencil.circle")
                            .font(.title2)
                    }
                }
            }
        }
    }
}

4. Selección Múltiple de Elementos

Otro caso de uso fantástico para el EditButton en SwiftUI es la selección múltiple. Imagina la aplicación de Correo (Mail), donde tocas “Editar” y aparecen círculos a la izquierda de cada correo para seleccionar varios y borrarlos de golpe.

Para lograr esto, simplemente pasas un enlace (Binding) a una propiedad de tipo Set en el inicializador de la List.

struct MultiSelectView: View {
    @State private var items = ["Tarea 1", "Tarea 2", "Tarea 3", "Tarea 4"]
    // Conjunto para almacenar los IDs de los elementos seleccionados
    @State private var seleccion = Set<String>()
    
    var body: some View {
        NavigationView {
            VStack {
                // Pasamos el binding 'selection' a la lista
                List(selection: $seleccion) {
                    ForEach(items, id: \.self) { item in
                        Text(item)
                    }
                }
                .navigationTitle("Tareas")
                .toolbar {
                    EditButton()
                }
                
                if !seleccion.isEmpty {
                    Button(role: .destructive, action: borrarSeleccionados) {
                        Text("Borrar \(seleccion.count) tareas")
                            .frame(maxWidth: .infinity)
                            .padding()
                            .background(Color.red.opacity(0.1))
                            .cornerRadius(10)
                    }
                    .padding()
                }
            }
        }
    }
    
    func borrarSeleccionados() {
        items.removeAll { seleccion.contains($0) }
        // Limpiamos la selección tras borrar
        seleccion.removeAll()
    }
}

Al tocar el EditButton, SwiftUI cambiará automáticamente la interfaz para mostrar las casillas de verificación en lugar de los botones de borrar individuales.


5. El EditButton en un Entorno Multiplataforma

SwiftUI promete “Aprende una vez, aplícalo en cualquier lugar”. Sin embargo, un buen iOS Developer sabe que las convenciones de interfaz de usuario varían enormemente entre iOS, macOS y watchOS. Veamos cómo se comporta el EditButton en cada entorno utilizando Xcode.

5.1. iOS y iPadOS

Este es el hábitat natural del EditButton. Funciona exactamente como hemos descrito a lo largo del tutorial. Se integra perfectamente en las barras de navegación, admite gestos de deslizamiento (swipe-to-delete) incluso sin estar en modo edición, y las animaciones son fluidas.

5.2. macOS

Aquí es donde la realidad golpea duro. En macOS, no existe el concepto tradicional del EditButton para listas.

Los usuarios de Mac no esperan hacer clic en un botón de “Editar” para borrar un elemento de una lista. Esperan:

  1. Hacer clic en un elemento para seleccionarlo y presionar la tecla Retroceso (Backspace) o Suprimir.
  2. Hacer clic derecho y seleccionar “Eliminar” en un menú contextual.

Si intentas compilar un EditButton() en un target de macOS en Xcode, simplemente no hará nada útil o ni siquiera se mostrará de forma estándar. En macOS, la eliminación se maneja mejor añadiendo un menú contextual y comandos de teclado:

// Ejemplo adaptado para macOS
List(selection: $seleccion) {
    ForEach(items, id: \.self) { item in
        Text(item)
            .contextMenu {
                Button("Eliminar") {
                    // Lógica de borrado
                }
            }
    }
}
.onDeleteCommand {
    // Se ejecuta al pulsar la tecla Suprimir en Mac
    borrarSeleccionados()
}

5.3. watchOS

En el Apple Watch, el espacio es vital. El EditButton sí está disponible en watchOS, pero requiere un cuidado especial.

Generalmente, las listas en watchOS prefieren las acciones de deslizamiento (swipe actions). Sin embargo, si necesitas un modo de edición masiva o reordenamiento, puedes usarlo. En lugar de una barra de navegación superior (que apenas existe en watchOS), el botón suele colocarse en la barra de herramientas (.toolbar) y el sistema decidirá el mejor lugar para mostrarlo, a menudo requiriendo que el usuario desplace la vista hacia arriba.


6. Integración con Core Data y SwiftData

Cuando trabajas con persistencia de datos, borrar un elemento de un simple array de Strings no es suficiente. Debes asegurarte de eliminar el objeto real de la base de datos.

Si usas el nuevo y potente framework SwiftData junto con SwiftUI, el .onDelete funciona de forma gloriosa y limpia.

import SwiftUI
import SwiftData

struct BaseDeDatosView: View {
    // Obtenemos el contexto de SwiftData
    @Environment(\.modelContext) private var context
    // Consultamos los datos
    @Query private var notas: [Nota]
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(notas) { nota in
                    Text(nota.titulo)
                }
                .onDelete(perform: borrarNota)
            }
            .navigationTitle("Mis Notas")
            .toolbar {
                EditButton()
            }
        }
    }
    
    func borrarNota(at offsets: IndexSet) {
        for index in offsets {
            let notaABorrar = notas[index]
            context.delete(notaABorrar) // Borramos de la base de datos
        }
        // Nota: No necesitas guardar explícitamente, SwiftData tiene autosave
    }
}

Aquí vemos el verdadero poder de la programación Swift: código conciso, legible y extremadamente eficiente.


Conclusión

El EditButton en SwiftUI es mucho más que un simple botón; es la puerta de entrada a todo un subsistema de manipulación de listas integrado en el entorno de SwiftUI. Atrás quedaron los días de implementar cinco métodos de delegado diferentes en Xcode solo para permitir que un usuario borre una fila.

Entender cómo el Environment maneja el editMode, saber cuándo utilizar métodos de selección múltiple, y, sobre todo, comprender las diferencias filosóficas de interfaz entre iOS, macOS y watchOS, te separará de un programador principiante y te consolidará como un iOS Developer versátil y profesional.

La programación Swift sigue evolucionando para hacer que las intenciones del desarrollador se traduzcan en código lo más rápido posible. Domina estas herramientas y tus aplicaciones no solo serán más robustas, sino mucho más agradables de construir.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

@discardableResult en Swift

Next Article

GlassEffectContainer en SwiftUI

Related Posts