Programación en Swift y SwiftUI para iOS Developers

Dominando el tamaño de las ventanas en visionOS con SwiftUI

Si eres un iOS developer experimentado, llevas años programando con una constante inamovible: el tamaño de la pantalla es fijo. Ya sea un iPhone SE o un iPad Pro de 12.9 pulgadas, el hardware dicta los límites. Tu trabajo en Xcode consistía en adaptar tu contenido a esos rectángulos predefinidos.

Bienvenido a la computación espacial. En visionOS, esa certeza desaparece. El lienzo es infinito y las ventanas pueden tener cualquier tamaño, estar en cualquier lugar y, lo más importante, el usuario puede redimensionarlas a su antojo.

En este tutorial vamos a desglosar todo lo que necesitas saber sobre el tamaño de ventanas en visionOS y SwiftUI. Aprenderás a configurar tamaños iniciales, establecer restricciones, manejar volúmenes 3D y adaptar tu programación Swift para crear experiencias espaciales fluidas.


El Cambio de Paradigma: De UIWindow a WindowGroup Espacial

En el desarrollo clásico de iOS con UIKit o SwiftUI, la ventana (Window) solía ser sinónimo de la pantalla del dispositivo. En visionOS, una aplicación puede tener múltiples ventanas abiertas simultáneamente, y cada una es una entidad flotante independiente.

Para empezar, debemos entender cómo SwiftUI define una ventana en este nuevo sistema operativo. Todo comienza en el archivo principal de tu App, dentro del WindowGroup.

import SwiftUI

@main
struct VisionOSApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        // Aquí es donde la magia del tamaño ocurre
        .defaultSize(width: 600, height: 400)
    }
}

A diferencia de iOS, donde el sistema lanza la app a pantalla completa, en visionOS tú sugieres el tamaño inicial. Pero esto es solo la punta del iceberg.


Controlando el Tamaño Inicial: .defaultSize

El modificador .defaultSize es tu primera herramienta en Xcode para sugerir cómo debe presentarse tu aplicación. Es crucial entender que esto es una sugerencia. El sistema intentará respetarla, pero factores como la posición del usuario o la presencia de otras apps podrían influir.

Existen varias formas de definir este tamaño en tu programación Swift:

1. Tamaño Fijo en Puntos

La forma más común para ventanas planas (estilo pizarra o “slate”). Recuerda que en visionOS, los puntos se traducen a metros físicos dependiendo de la distancia, pero para el diseño de UI, sigues pensando en puntos lógicos.

WindowGroup {
    ContentView()
}
.defaultSize(width: 800, height: 600)

2. Tamaño para Volúmenes (3D)

Si estás desarrollando una aplicación que utiliza un volumen (una caja 3D delimitada), necesitas definir tres dimensiones: anchura, altura y profundidad.

WindowGroup(id: "SolarSystem") {
    SolarSystemView()
}
.windowStyle(.volumetric)
.defaultSize(width: 1.0, height: 1.0, depth: 1.0, in: .meters)

Nota para el iOS developer: Aquí introducimos unidades físicas explícitas (.meters). Esto es vital para objetos que deben tener una escala realista en el mundo del usuario.


Restricciones de Redimensionamiento: .windowResizability

Uno de los mayores desafíos en el tamaño de ventanas en visionOS y SwiftUI es manejar lo que el usuario puede hacer. Por defecto, las ventanas en visionOS tienen una “barra de manejo” en la esquina inferior que permite al usuario estirar la ventana libremente.

¿Qué pasa si tu diseño se rompe si la ventana es muy estrecha? ¿O si se ve ridículo si es demasiado ancha? Aquí entra .windowResizability.

Bloquear el Tamaño al Contenido

Si quieres que tu ventana se ajuste exactamente al tamaño de tu vista de SwiftUI y no permita al usuario cambiarlo, utiliza .contentSize.

WindowGroup {
    VStack {
        Text("Esta ventana no se puede estirar")
        Image("Logo")
    }
    .frame(width: 400, height: 300) // El tamaño de la vista manda
}
.windowResizability(.contentSize)

Esto es útil para diálogos, alertas personalizadas o herramientas utilitarias pequeñas.

Límites Mínimos y Máximos

A menudo, querrás permitir cierta flexibilidad, pero dentro de unos límites lógicos. Puedes usar el modificador frame(minWidth:maxWidth:) dentro de tu vista, y decirle a la ventana que respete esos límites.

struct ResizableContentView: View {
    var body: some View {
        NavigationSplitView {
            List(1...10, id: \.self) { item in
                Text("Item \(item)")
            }
        } detail: {
            Text("Detalle")
        }
        // Definimos los límites lógicos de nuestra UI
        .frame(minWidth: 600, maxWidth: 1200, minHeight: 400, maxHeight: 800)
    }
}

// En tu App.swift
WindowGroup {
    ResizableContentView()
}
.windowResizability(.contentSize)

Al combinar .windowResizability(.contentSize) con las restricciones de frame en la vista raíz, visionOS entiende que la barra de redimensionamiento debe detenerse cuando el usuario alcanza esos tamaños mínimos o máximos.


Adaptando el Diseño con GeometryReader

Como experto en SwiftUI, ya conoces GeometryReader. En visionOS es más importante que nunca. Dado que el usuario puede redimensionar la ventana en tiempo real, tu diseño debe ser responsivo.

Una técnica común en programación Swift para visionOS es cambiar el layout de HStack a VStack dependiendo del ancho disponible, similar a como lo harías en iPad con Slide Over.

struct ResponsiveView: View {
    var body: some View {
        GeometryReader { proxy in
            if proxy.size.width > 600 {
                HStack {
                    SideBar()
                    MainContent()
                }
            } else {
                VStack {
                    TopBar()
                    MainContent()
                }
            }
        }
    }
}

Esto asegura que tu aplicación se sienta nativa y bien construida, independientemente de cómo el usuario decida configurar su espacio de trabajo.


Profundidad y Escala: El Eje Z

Aquí es donde el iOS developer suele tropezar. Las ventanas en visionOS tienen profundidad, incluso las planas. Cuando usas .glassBackgroundEffect(), el sistema añade grosor y sombras.

Si estás trabajando con Volumetric Windows, el tamaño es literal. Un cubo de 1 metro es enorme en una habitación pequeña. Debes usar la herramienta de previsualización de Xcode para entender la escala humana.

Redimensionamiento de Volúmenes

Para volúmenes, la redimensionabilidad funciona diferente. A veces quieres que el contenido se escale (se haga más grande visualmente) y otras veces quieres que el contenedor se agrande para mostrar más contenido.

WindowGroup {
    My3DModelView()
}
.windowStyle(.volumetric)
.defaultSize(width: 0.5, height: 0.5, depth: 0.5, in: .meters)
.windowResizability(.contentSize) // Evita que el usuario distorsione el modelo 3D

Programación Avanzada: Redimensionamiento Dinámico

¿Qué pasa si necesitas cambiar el tamaño de la ventana programáticamente después de que se ha abierto? Por ejemplo, el usuario pulsa un botón “Ver detalles” y la ventana necesita expandirse.

Hasta las versiones iniciales de visionOS, no podías forzar un cambio de tamaño arbitrario en una ventana existente tan fácilmente como frame.size = newSize en UIKit. La filosofía de Apple es la agencia del usuario.

Sin embargo, puedes influir en el tamaño a través del estado de SwiftUI si has configurado .windowResizability(.contentSize).

struct DynamicWindowView: View {
    @State private var isExpanded = false
    
    var body: some View {
        VStack {
            Toggle("Expandir ventana", isOn: $isExpanded)
            
            if isExpanded {
                Text("Contenido adicional...")
                    .frame(height: 200)
            }
        }
        .frame(width: 400, height: isExpanded ? 600 : 300) // El frame cambia dinámicamente
        .animation(.spring(), value: isExpanded)
    }
}

Si la ventana tiene la propiedad de redimensionamiento atada al contenido, al cambiar el frame de la vista raíz mediante una animación de SwiftUI, la ventana física de visionOS se animará suavemente para acomodar el nuevo tamaño. ¡Magia pura de SwiftUI!


Mejores Prácticas (Human Interface Guidelines)

Para optimizar tu app y que destaque en la App Store, sigue estas reglas de oro:

  1. No uses pantalla completa: No intentes llenar el campo de visión del usuario. Las ventanas deben ser cómodas de ver sin mover el cuello.
  2. Respeta los márgenes: El material de “cristal” (Glass) necesita espacio para respirar. Usa .padding() generosamente. Si tu contenido toca los bordes, el efecto de profundidad se pierde y el redimensionamiento se ve extraño.
  3. Tamaños ergonómicos: Un ancho predeterminado de 1280pt suele ser un buen máximo para contenido legible. Más ancho y el usuario tendrá que girar la cabeza para leer de un lado a otro.
  4. Consistencia: Si el usuario redimensiona tu ventana, visionOS recordará ese tamaño la próxima vez que abra la app. No luches contra esto forzando un tamaño al inicio cada vez.

Troubleshooting en Xcode para Desarrolladores

Al trabajar con el tamaño de ventanas en visionOS y SwiftUI, te encontrarás con estos errores comunes:

  • El contenido se corta: Probablemente estás usando un .frame fijo dentro de una ventana redimensionable. Cambia a minWidth y maxWidth.
  • La ventana no cambia de tamaño con el contenido: Asegúrate de haber aplicado el modificador .windowResizability(.contentSize) al WindowGroup en tu archivo App.swift. Sin esto, la ventana se queda en su tamaño actual aunque el contenido interno cambie.
  • Ornaments desalineados: Si cambias el tamaño de la ventana drásticamente, revisa tus Ornaments. A veces necesitan anclajes personalizados para no solaparse con el contenido nuevo.

Conclusión

Dominar el tamaño de ventanas en visionOS y SwiftUI es la habilidad que diferenciará a un iOS developer promedio de un pionero en computación espacial. La clave está en dejar de pensar en píxeles estáticos y empezar a pensar en restricciones fluidas y contenido adaptable.

La combinación de WindowGroup, defaultSize y windowResizability te da un control total sobre cómo se siente tu aplicación en el espacio físico. Ahora, abre Xcode, experimenta con estos modificadores y crea ventanas que se sientan mágicas.

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

Foundation Models en Swift

Next Article

Dependency Injection en SwiftUI

Related Posts