Programación en Swift y SwiftUI para iOS Developers

Cómo obtener el tamaño de pantalla en SwiftUI

En el fascinante mundo del desarrollo de aplicaciones para el ecosistema de Apple, la creación de interfaces de usuario adaptables y responsivas es una habilidad fundamental para cualquier iOS Developer. Con la evolución de los dispositivos, desde las diminutas pantallas del Apple Watch hasta los amplios monitores de los Mac, saber cómo obtener el tamaño de pantalla en SwiftUI se ha convertido en un requisito indispensable en la programación Swift.

Si vienes del mundo de UIKit, probablemente estabas acostumbrado a llamar a UIScreen.main.bounds y dar el problema por resuelto. Sin embargo, en SwiftUI, el paradigma ha cambiado drásticamente. Las aplicaciones ahora pueden ejecutarse en modo Split View en el iPad, en ventanas redimensionables en macOS o en diferentes modelos de Apple Watch. Por lo tanto, el tamaño de la “pantalla” no siempre equivale al tamaño disponible para tu aplicación.

En este extenso tutorial de programación Swift, exploraremos a fondo las mejores prácticas, técnicas y herramientas disponibles en Xcode para dominar el manejo de dimensiones y tamaños en SwiftUI para iOS, macOS y watchOS.


1. El Cambio de Paradigma: De UIKit a SwiftUI

Antes de profundizar en el código Swift, es crucial entender por qué SwiftUI maneja los tamaños de manera diferente a sus predecesores.

En UIKit, el enfoque era imperativo: le decías a la interfaz exactamente dónde colocarse basándote en las coordenadas absolutas de la pantalla. En SwiftUI, el enfoque es declarativo. El framework utiliza un proceso de negociación de diseño (layout process) que consta de tres pasos:

  1. La vista padre propone un tamaño a la vista hija.
  2. La vista hija elige su propio tamaño (basado en sus necesidades y en la propuesta del padre).
  3. La vista padre posiciona a la vista hija en su sistema de coordenadas.

Debido a esto, preguntar explícitamente por el tamaño de la pantalla física a menudo es un “antipatrón” en SwiftUI. En su lugar, deberíamos preguntar por el espacio disponible para nuestra vista. Sin embargo, hay escenarios legítimos (como el posicionamiento de elementos de fondo, cálculos de escala complejos o físicas de juegos) donde un iOS Developer necesita saber las dimensiones exactas.


2. El Enfoque Clásico (Y por qué debes evitarlo en el futuro)

La forma más rápida y conocida por los veteranos de la programación Swift de obtener el tamaño de la pantalla ha sido históricamente mediante UIScreen.

import SwiftUI

struct ScreenSizeClassicView: View {
    // Obteniendo el tamaño (No recomendado para apps modernas multiplataforma)
    let screenWidth = UIScreen.main.bounds.width
    let screenHeight = UIScreen.main.bounds.height
    
    var body: some View {
        VStack {
            Text("Ancho de pantalla: \(screenWidth)")
            Text("Alto de pantalla: \(screenHeight)")
        }
        .padding()
        .background(Color.blue.opacity(0.2))
        .cornerRadius(10)
    }
}

Problemas con UIScreen.main.bounds

Aunque este código compilará en Xcode para iOS, presenta varios problemas graves:

  1. Deprecación: A partir de iOS 16, Apple ha marcado UIScreen.main como obsoleto. Las aplicaciones modernas pueden tener múltiples escenas (ventanas) activas a la vez.
  2. Falta de soporte multiplataforma: Este código fallará si intentas compilarlo para macOS o watchOS, ya que UIScreen es parte de UIKit, no de AppKit o WatchKit.
  3. Multitarea en iPad: Si el usuario pone tu app en Split View (pantalla dividida), UIScreen.main.bounds te dará el tamaño físico de la pantalla del iPad, no el espacio de la mitad de la pantalla donde tu app se está ejecutando, rompiendo tu diseño.

3. La Solución Nativa de SwiftUI: GeometryReader

Si te preguntas cómo obtener el tamaño de pantalla en SwiftUI de la manera correcta y respetando el flujo de layout del framework, la respuesta es GeometryReader.

GeometryReader es una vista que toma todo el espacio que se le ofrece y proporciona a su contenido un objeto GeometryProxy, el cual contiene información sobre el tamaño y el área segura (safe area) disponibles.

import SwiftUI

struct GeometryReaderExampleView: View {
    var body: some View {
        GeometryReader { geometry in
            VStack(spacing: 20) {
                Text("Ancho disponible: \(geometry.size.width)")
                Text("Alto disponible: \(geometry.size.height)")
                
                // Usando las proporciones para crear un diseño responsivo
                Rectangle()
                    .fill(Color.orange)
                    .frame(width: geometry.size.width * 0.8, height: geometry.size.height * 0.3)
                    .overlay(Text("80% Ancho / 30% Alto").foregroundColor(.white))
            }
            // Centrando el contenido dentro del GeometryReader
            .frame(width: geometry.size.width, height: geometry.size.height)
        }
        // Ignorando los bordes seguros si queremos el tamaño total de la pantalla en un dispositivo móvil
        .edgesIgnoringSafeArea(.all) 
    }
}

Ventajas de GeometryReader

  • Totalmente compatible: Funciona a la perfección en SwiftUI para iOS, macOS y watchOS sin cambiar una sola línea de código.
  • Responde a cambios de entorno: Si giras el dispositivo de vertical a horizontal, o cambias el tamaño de la ventana en Mac, el GeometryReader recalcula y actualiza la vista instantáneamente.
  • Respeta la multitarea: En un iPad en pantalla dividida, geometry.size reflejará el tamaño de la porción de pantalla asignada a tu app.

Precauciones como iOS Developer

GeometryReader es una herramienta muy “codiciosa” (greedy). A diferencia de un VStack o un Text que se encogen para ajustarse a su contenido, GeometryReader intentará ocupar todo el espacio que el padre le ofrezca. Usarlo indiscriminadamente puede llevar a diseños colapsados o comportamientos inesperados en Xcode. Úsalo solo cuando realmente necesites leer proporciones o dimensiones relativas.


4. Multiplataforma: Obteniendo el Tamaño Físico Real en iOS, macOS y watchOS

Si tu requerimiento exige estrictamente saber la resolución de hardware (por ejemplo, para enviar analíticas del dispositivo o inicializar un motor gráfico personalizado en Swift), debes adaptar tu código según el sistema operativo.

Afortunadamente, la programación Swift moderna utiliza macros de compilación condicional (#if os(...)) que nos permiten compilar diferentes bloques de código según la plataforma destino en Xcode.

Vamos a crear un “Wrapper” o extensión que cualquier iOS Developer puede llevar en su caja de herramientas para obtener el tamaño de pantalla en cualquier dispositivo Apple.

import SwiftUI

#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#elseif canImport(WatchKit)
import WatchKit
#endif

extension View {
    /// Devuelve el tamaño físico de la pantalla primaria según la plataforma.
    func getDeviceScreenSize() -> CGSize {
        #if os(iOS) || os(tvOS)
        // Para iOS 15 o superior, es mejor iterar sobre las escenas conectadas
        if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
            return windowScene.screen.bounds.size
        }
        return CGSize.zero
        
        #elseif os(macOS)
        // En macOS obtenemos la pantalla principal
        if let screen = NSScreen.main {
            return screen.frame.size
        }
        return CGSize.zero
        
        #elseif os(watchOS)
        // En watchOS usamos el dispositivo actual
        return WKInterfaceDevice.current().screenBounds.size
        
        #else
        return CGSize.zero
        #endif
    }
}

Cómo usar esta extensión en SwiftUI

Ahora que hemos abstraído la complejidad de las diferentes plataformas (UIKit, AppKit, WatchKit), podemos usar esta extensión de manera limpia en nuestras vistas.

struct MultiplatformScreenSizeView: View {
    @State private var screenSize: CGSize = .zero
    
    var body: some View {
        VStack {
            Text("Resolución Física del Dispositivo:")
                .font(.headline)
            Text("\(screenSize.width, specifier: "%.0f") x \(screenSize.height, specifier: "%.0f") px")
                .font(.system(size: 24, weight: .bold, design: .monospaced))
                .foregroundColor(.green)
        }
        .onAppear {
            // Calculamos el tamaño una vez que la vista aparece
            self.screenSize = getDeviceScreenSize()
        }
    }
}

Este enfoque garantiza que tu código compile en Xcode sin importar el destino que selecciones (iPhone, Mac, Apple Watch), consolidando tu nivel avanzado en programación Swift.


5. El Futuro de SwiftUI: Variables de Entorno y modificadores avanzados

Apple actualiza el framework de SwiftUI cada año. Para los desarrolladores que apuntan a versiones más recientes (iOS 16+ / macOS 13+), el uso de Variables de Entorno (@Environment) está sustituyendo muchas necesidades de lectura de métricas físicas.

Aunque todavía no existe un @Environment(\.screenSize) directo, Apple proporciona herramientas poderosas para adaptar el layout sin saber explícitamente los píxeles.

ViewThatFits

Si la razón por la que te preguntas cómo obtener el tamaño de pantalla en SwiftUI es simplemente para decidir si muestras un layout vertical u horizontal, ViewThatFits (introducido en iOS 16) es la solución perfecta que todo iOS Developer debe dominar.

struct AdaptiveLayoutView: View {
    var body: some View {
        // ViewThatFits evaluará las opciones y elegirá la primera que no se recorte.
        ViewThatFits {
            // Opción 1: Horizontal (si hay espacio suficiente)
            HStack {
                FeatureCard(title: "Rápido", icon: "bolt")
                FeatureCard(title: "Seguro", icon: "lock")
                FeatureCard(title: "Fiable", icon: "shield")
            }
            
            // Opción 2: Vertical (si la pantalla es estrecha, como un iPhone SE o un Watch)
            VStack {
                FeatureCard(title: "Rápido", icon: "bolt")
                FeatureCard(title: "Seguro", icon: "lock")
                FeatureCard(title: "Fiable", icon: "shield")
            }
        }
    }
}

// Subvista de ayuda
struct FeatureCard: View {
    let title: String
    let icon: String
    
    var body: some View {
        VStack {
            Image(systemName: icon).font(.largeTitle)
            Text(title)
        }
        .padding()
        .background(Color.secondary.opacity(0.2))
        .cornerRadius(12)
    }
}

Con ViewThatFits, el framework realiza el cálculo del tamaño por ti de manera interna, manteniendo tu código de Swift limpio, declarativo y sumamente eficiente.


6. Buenas Prácticas y Consejos para el iOS Developer

A medida que dominas la programación Swift y profundizas en las entrañas de Xcode, es vital adoptar una mentalidad de diseño fluido (Fluid Design). Aquí tienes los mandamientos clave relacionados con el tamaño de pantalla en SwiftUI:

  1. Evita los números mágicos: No configures .frame(width: 320, height: 480). Lo que se ve bien en un iPhone 13 Pro Max se verá recortado en un iPhone SE o diminuto en un iPad. Usa márgenes relativos (.padding()), Spacer() y proporciones mediante GeometryReader.
  2. Prioriza Layouts Flexibles: Utiliza HStack, VStack, Grid (iOS 16+) y déjale a SwiftUI la responsabilidad de los cálculos matemáticos del layout.
  3. El Contexto Importa: Recuerda que con el Stage Manager de macOS y del iPadOS, las aplicaciones ahora viven en ventanas completamente flexibles. Preguntar por el tamaño del monitor principal rara vez será útil para posicionar un botón. Diseña para la ventana (Window), no para la pantalla (Screen).
  4. Prueba exhaustivamente en Xcode Previews: Aprovecha el Canvas de Xcode. Puedes configurar múltiples previsualizaciones simultáneas para el mismo código apuntando a un iPhone, un iPad y un Apple Watch para ver cómo responde tu código a las distintas métricas de pantalla.
// Ejemplo de múltiples previews
#Preview("iPhone") {
    GeometryReaderExampleView()
}

#Preview("iPad Split View", traits: .landscapeLeft) {
    GeometryReaderExampleView()
}

Conclusión

Saber cómo obtener el tamaño de pantalla en SwiftUI de manera efectiva separa a un novato de un verdadero profesional de la programación Swift. Hemos repasado por qué el antiguo método de UIScreen está quedando obsoleto, cómo GeometryReader es la navaja suiza para medidas relativas, y cómo abstraer el hardware subyacente para crear componentes verdaderamente universales en iOS, macOS y watchOS.

Leave a Reply

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

Previous Article

Preguntas para una entrevista de trabajo como Senior iOS Developer

Related Posts