El iPhone Duo introduce un problema de diseño que no se resuelve simplemente adaptando la interfaz al ancho disponible. Cuando el dispositivo está parcialmente plegado, una vista puede quedar justo encima de la bisagra. Y aunque SwiftUI siga distribuyendo sus elementos correctamente desde el punto de vista geométrico, leer un texto o pulsar un botón situado en esa zona resulta incómodo.
Con iOS 27.1, SwiftUI incorpora una herramienta para abordar este escenario: las regiones reservadas (ReservedRegion). Nos permiten saber qué partes del espacio de una vista están ocupadas o divididas por elementos que no controlamos, como una cámara o el pliegue de una pantalla.
Dos tipos de regiones reservadas
La API distingue entre dos situaciones:
.occlusion: una zona tapada por algún elemento, como una cámara o la Dynamic Island..division: una zona que separa el contenido en partes, como la bisagra del iPhone Duo.
Contenedores del sistema, como ArrangementView, ya tienen en cuenta estas características. Sin embargo, una HStack, una LazyVGrid o una composición personalizada no reorganizan necesariamente sus elementos para esquivar la bisagra. Ahí es donde necesitamos intervenir.
Detectar la bisagra con GeometryProxy
Imaginemos una aplicación que muestra un panel de indicadores de actividad. Queremos presentar cuatro tarjetas en una cuadrícula, dos a cada lado del pliegue vertical, sin que ninguna quede atravesada por él.
Podemos detectar la región desde onGeometryChange y almacenar solo la información que influye en el diseño:
import SwiftUI
struct FoldInfo: Equatable {
var width: CGFloat = 0
var horizontalRange: ClosedRange<CGFloat>?
var isActive = false
}
struct ActivityDashboard: View {
@State private var foldInfo = FoldInfo()
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(0..<12, id: \.self) { index in
VStack(alignment: .leading, spacing: 8) {
Text("Indicador \(index + 1)")
.font(.caption)
Image(systemName: "chart.bar.fill")
.font(.title)
}
.frame(maxWidth: .infinity, minHeight: 100)
.padding(8)
.background(.quaternary, in: .rect(cornerRadius: 12))
}
}
.onGeometryChange(for: FoldInfo.self) { proxy in
let fold = proxy.reservedRegions(
kind: .division,
options: .includeInactive
).first { $0.frame.height > $0.frame.width }
return FoldInfo(
width: proxy.size.width,
horizontalRange: fold.map {
$0.frame.minX...$0.frame.maxX
},
isActive: fold?.isActive ?? false
)
} action: { foldInfo = $0 }
.padding(20)
}
}
private var columns: [GridItem] {
let normalGap: CGFloat = 16
guard let fold = foldInfo.horizontalRange else {
return [.init(.adaptive(minimum: 140), spacing: normalGap)]
}
if !foldInfo.isActive {
return Array(repeating: .init(.flexible(), spacing: normalGap), count: 4)
}
// Ajustamos las cuatro columnas al lado más estrecho.
let left = fold.lowerBound
let right = foldInfo.width - fold.upperBound
let columnWidth = max(1, (min(left, right) - normalGap) / 2)
let centralGap = max(normalGap, foldInfo.width - 4 * columnWidth - 2 * normalGap)
return [
.init(.fixed(columnWidth), spacing: normalGap),
.init(.fixed(columnWidth), spacing: centralGap),
.init(.fixed(columnWidth), spacing: normalGap),
.init(.fixed(columnWidth))
]
}
}
La clave está en la separación entre la segunda y la tercera columna. La anchura de cada tarjeta se calcula según el lado más estrecho del pliegue y el espacio central crece lo necesario para dejar libre la bisagra. El frame que devuelve la región ya incluye los márgenes de seguridad recomendados, por lo que no debemos sumarlos otra vez.
¿Por qué consultar también las regiones inactivas?
Por defecto, reservedRegions(kind:) devuelve únicamente las regiones activas. En el iPhone Duo, la división se activa cuando plegamos parcialmente la pantalla interior; si está completamente abierta, desaparece de esa consulta.
El problema es que una cuadrícula adaptable podría mostrar cinco columnas con el dispositivo abierto y reorganizarse en cuatro al plegarlo. El contenido saltaría de posición justo cuando estamos cambiando la postura del dispositivo.
Con .includeInactive, podemos detectar el pliegue incluso cuando no está activo y consultar isActive para distinguir ambos estados. En el ejemplo conservamos cuatro columnas cuando la pantalla está abierta, y únicamente modificamos las anchuras y el espacio central cuando aparece la división. En una aplicación real conviene adaptar también el número de columnas al espacio y al tamaño de texto disponible, manteniendo preferiblemente una cantidad par.
No todas las vistas necesitan este tratamiento
Hay un detalle importante: una región horizontal puede atravesar temporalmente una lista con desplazamiento vertical, porque el usuario puede desplazar el contenido fuera de ella. Un pliegue vertical no se evita desplazándose hacia abajo. Por eso, antes de aplicar cálculos personalizados, merece la pena comprobar qué orientación afecta realmente a nuestra vista.
Además, Apple recomienda utilizar primero contenedores y presentaciones estándar. Las regiones reservadas son especialmente útiles cuando construimos cuadrículas, controles flotantes o diseños personalizados que necesitan colocar contenido con precisión. El sistema devuelve sus coordenadas en el espacio de la propia vista, no en coordenadas globales de pantalla, y el comportamiento predeterminado contempla la dirección de escritura.
Puedes profundizar en los detalles de reservedRegions y en las recomendaciones de Apple para diseñar interfaces adaptativas en iPhone Duo.
La enseñanza va más allá de un dispositivo plegable: un diseño adaptable no consiste solo en que todo quepa, sino en que cada elemento siga siendo legible, accesible y estable mientras cambia el espacio disponible.