Reordenación nativa de contenedores en SwiftUI con iOS 27
Arturo Rivas Arias
Hasta ahora, si queríamos permitir que los usuarios reorganizasen elementos en una interfaz SwiftUI, la solución más habitual era recurrir a List y su modificador onMove. El problema era que muchas interfaces modernas ya no utilizan las listas tradicionales: dashboards, cuadrículas, paneles personalizables, carruseles horizontales o layouts completamente personalizados quedaban fuera de esta experiencia.
Con iOS 27, SwiftUI introduce una nueva API que generaliza este comportamiento mediante los modificadores reorderable() y reorderContainer(...), permitiendo implementar una reordenación nativa en prácticamente cualquier contenedor de vistas. Esta novedad supone un cambio conceptual importante: la reordenación deja de ser una característica exclusiva de List para convertirse en una capacidad que cualquier contenedor puede adoptar.
Cómo funciona la nueva API
La nueva solución se divide en dos partes claramente diferenciadas. Por un lado, debemos indicar cuál es el contenido dinámico puede participar en una operación de reordenación:
ForEach(items) { item in
ItemView(item: item)
}
.reorderable()
Por otro lado, el contenedor padre debe declarar que admite operaciones de reordenación y definir cómo se actualizan los datos cuando se ejecuta la misma:
.reorderContainer(for: Item.self) { difference in
items.apply(difference: difference)
}
Esta separación encaja perfectamente con la filosofía declarativa de SwiftUI.
Un ejemplo práctico
Imaginemos que tenemos un lista de proyecto, pero que estamos usando un LazyVStack dentro de un ScrollView. Al no ser una lista, no tenemos disponible el modificador onMove. Pero a partir de iOS 27, podemos utilizar la nueva API.
import SwiftUI
struct Project: Identifiable, Sendable {
let id = UUID()
var name: String
}
struct ProjectsView: View {
@State private var projects = [
Project(name: "Backend"),
Project(name: "iOS"),
Project(name: "WatchOS"),
Project(name: "VisionOS")
]
var body: some View {
ScrollView {
LazyVStack(spacing: 12) {
ForEach(projects) { project in
Text(project.name)
}
.reorderable()
}
.reorderContainer(for: Project.self) { difference in
projects.apply(difference: difference)
}
}
}
}
Podría parece un cambio menor, pero con esta actualización podremos crear layouts más dinámicos sin estar atados a las limitaciones de List ni a realizar una implementación manual para la reordenación de elementos.