Primeros pasos con SwiftUI

1
6460
  1. Introducción
  2. Entorno
  3. Primeros pasos con SwiftUI
  4. Los elementos básicos de la interfaz
  5. Usando @State y @Binding
  6. Conclusiones
  7. Referencias

Introducción

Este tutorial nos ayudará a dar los primeros pasos con SwiftUI. Es una nueva forma declarativa y simple de crear interfaces de usuario en todas las plataformas de Apple usando solamente Swift. Puedes crear las interfaces de usuario para cualquier dispositivo Apple utilizando solo un API de SwiftUI. Con una sintaxis declarativa de Swift, que es fácil de leer y natural de escribir, SwiftUI te permite mantener su código y diseño perfectamente sincronizados con las nuevas herramientas de diseño de Xcode.

También SwiftUI te permite ignorar el Interface Builder (IB) sin tener que escribir instrucciones detalladas paso a paso para diseñar su interfaz de usuario. IB y Xcode eran aplicaciones separadas antes de Xcode 4, y las costuras aún se muestran cada vez que editas el nombre de un IBAction o IBOutlet o lo eliminas de su código, y tu aplicación se bloquea porque IB no ve cambios en el código. O te disgustan los identificadores de tipo String para «segues» o celdas de vista de tabla que tienes que usar en tu código, pero Xcode no puede comprobarlo porque son de tipo «string».

Y sí, es más rápido y fácil diseñar una nueva interfaz de usuario en un editor WYSIWYG, es mucho más eficiente copiar o editar la interfaz de usuario cuando está escrita en código declarativo. Puedes obtener una vista previa de una vista SwiftUI lado a lado con su código; un cambio en un lado actualizará el otro lado, por lo que siempre están sincronizados. No hay los identificadores tipo «string» para equivocarse. Y es código, pero mucho menos de lo que escribirías para UIKit, por lo que es más fácil de entender, editar y depurar.

SwiftUI no reemplaza a UIKit: como Swift y Objective-C, puedes usar ambos en la misma aplicación. No podrás ejecutar aplicaciones SwiftUI iOS en macOS. Para eso se necesita usar Catalyst (un framework de Apple). Pero, las API de SwiftUI son consistentes en todas las plataformas, por lo que será más fácil desarrollar la misma aplicación en múltiples plataformas que usan el mismo código fuente en cada una. Lo malo es que SwiftUI es solo compatible con iOS 13, pero con el tiempo esta desventaja se va diminuyendo.

Entorno

Este tutorial está escrito usando el siguiente entorno:

    • Hardware: MacBook Pro 15’ (2,5 GHz Intel Core i7, 16GB DDR3, mediados de 2015)
    • Sistema operativo: macOS Catalina 10.15
    • Versiones del software:
      • Xcode: 11
      • iOS SDK: 13.4

Es importante saber que para probar SwiftUI se necesita instalar Mac OS Catalina 10.15 y Xcode 11.

Primeros pasos con SwiftUI

Aunque los storyboards y XIB nos han servido bien, no son perfectos: pueden ser complicados de usar con el control de código fuente, hacen que sea difícil, si no imposible, moverse entre el código y los diseños visuales, y confían en un sistema inestable de conexiones mediante acciones y outlets (IBAction y IBOutlet).

SwiftUI barre todo eso de varias maneras importantes:

Hay una nueva estructura de interfaz de usuario declarativa que define cómo se ven y funcionan nuestros diseños.
La actualización de la vista previa de la IU genera automáticamente un nuevo código de Swift, y al cambiar el código de Swift se actualiza la vista previa de la IU.
Cualquier enlace en Swift, por ejemplo, los outlets y las acciones, efectivamente, ahora se verifican en tiempo de compilación, por lo que no hay más riesgo de que la IU falle por sorpresa en tiempo de ejecución.
Aunque utiliza controles de UIKit y AppKit por debajo, se ubica encima de ellos, lo que hace que el framework de la interfaz de usuario subyacente sea un detalle de implementación en lugar de algo que nos interesa específicamente.

Para conocer mejor el nuevo mundo de SwiftUI vamos a crear un proyecto muy simple. En Xcode 11 beta, crea un nuevo proyecto Xcode (Shift-Command-N), selecciona iOS App Aplicación de vista única, nombra el proyecto como quieras, luego marca la casilla «Use SwiftUI»:

En el navegador de proyecto, abre el grupo SwiftUI para ver lo que tiene: el antiguo AppDelegate.swift ahora se divide en AppDelegate.swift y SceneDelegate.swift. SceneDelegate no es específico de SwiftUI, pero esta línea sí lo es:

window.rootViewController = UIHostingController(rootView: ContentView())

UIHostingController permite integrar vistas SwiftUI en una aplicación existente. Cuando se inicia la aplicación, la ventana muestra una instancia de ContentView, que se define en ContentView.swift. Es una estructura que se ajusta al protocolo View.

struct ContentView: View {
  var body: some View {
    Text("Hello World")
  }
}

Este código declara que el cuerpo de ContentView contiene una vista de texto que muestra Hello World. Abajo en el bloque DEBUG, ContentView_Previews produce una vista que contiene una instancia de ContentView.

#if DEBUG
struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}
#endif

Aquí es donde puede especificar datos de muestra para la vista previa. Pero, ¿dónde está esta vista previa?
Haz clic en «Resume» y espera un momento para ver la vista previa:

El único archivo que no aparece en la lista es Main.storyboard: creará su interfaz de usuario en el código SwiftUI, vigilando la vista previa para ver cómo se ve. Pero no te preocupes, ¡no escribirá cientos de líneas de código para crear vistas!
SwiftUI es declarativo: tienes solo que declarar las vistas que quieras y SwiftUI convierte sus declaraciones en un código eficiente que hace el trabajo. Apple lo alienta a crear tantas vistas como necesite, para mantener su código fácil de leer y mantener. Las vistas reutilizables y parametrizadas se recomiendan especialmente: es cómo extraer código en una función, y es fácil de hacer con dos clicks.

Los elementos básicos de SwiftUI

Vamos a añadir más elementos visuales a nuestra vista. Para añadir más elementos a la vista tenemos que usar los contenedores como VStack o HStack. Son contenedores que distribuyen las vistas verticalmente o horizontalmente. La manera mas fácil de añadirlos es hacer Command-Click a la única vista que tenemos que es TextEntonces aparecerá el menú que nos permite fácilmente integrar nuestra única vista a HStack o a VStack.

Para añadir mas vistas tenemos que pulsar el botón arriba a la derecha. Podemos elegir entre Text, TextField, DatePicker, Slider, Toggle y, por supuesto, Button. También podemos editar las propiedades de cada vista a través del mismo menu, pulsando «Show SwiftUI Inspector». Cada vista tiene su conjunto de propiedades y para modificarlas hay que añadir modificadores. Por ejemplo, la fuente, el color, la alineación etc. También se puede hacer editando el código.

struct ContentView : View {
    
    var body: some View {
        HStack {
            Text("Hello World")
                .fontWeight(.light)
                .foregroundColor(Color.red)
                .multilineTextAlignment(.center)
        }
    }
}

Igualmente puedes hacer Command-Click en el código sobre un elemento y te sale el menú que te ayuda integrar tu vista dentro de los contenedores, conseguir ayuda rápida o incluso hacer algún refactoring. Este menú es muy útil y ayuda bastante sobre todo al principio.

 

Añadimos Button y centramos nuestras vistas. Además hemos añadido también el modificador spacing para meter el espacio entre los elementos de nuestro stack.

struct ContentView : View {
    
    var body: some View {
        VStack(alignment: .center, spacing: 10.0) {
            Text("Hello")
            Button(action: {}) {
                Text("Show Alert").foregroundColor(Color.red)
            }
        }
    }
}

¿Y si queremos vincular nuestra vista con otra? Si queremos crear un stack de vistas como lo hacíamos antes con el NavigationController, también tenemos que añadir otras declaraciones. Todo en SwiftUI se hace a través de las declaraciones. Al principio puede parecer muy inusual e incómodo, pero con el tiempo te vas acostumbrando y realmente es bastante fácil y rápido. Solo es otra manera de hacer las cosas.

Tenemos que envolver nuestro contenedor VStack con las vistas en NavigationView y añadir un elemento que se llama NavigationLink. El código es muy explicativo y fácil de entender. NavigationLink es un botón que necesita solo el destino (otra vista) y el texto. Y para demostrar el titulo de «la barra de navegación» adjuntamos un modificador «navigationBarTitle».

struct ContentView : View {
    var body: some View {
        NavigationView {
            VStack(alignment: .center, spacing: 10.0) {
                Text("Hello")
                Button(action:{}) {
                    Text("Show Alert")
                        .foregroundColor(Color.red)
                }
                NavigationLink(destination: DetailView()) {
                    Text("Go to Detail View")
                }
            }.navigationBarTitle("Main View")
        }
    }
}

Y solo con unas quince lineas de código tenemos la navegación entre dos vistas, la barra de navegación y un botón que no hace nada 😉

Usando property wrappers @State y @Binding

Cada vista en Swift UI es una estructura, así que puedes añadir y usar constantes y variables «normales» en SwiftUI. ¿Y si tenemos las vistas que dependen de los valores de  nuestras variables? ¿Cómo podemos sincronizar las vistas y las variables? Para eso tienes que usar una variable @State o @Binding si la UI debe actualizarse siempre que cambie su valor. Son «variables vivas» 😉 . Con la introducción de Swift 5.1 ahora tenemos el property wrapper: una cosa fundamental en SwiftUI. El property wrapper es una forma de añadir más funcionalidad a la propiedad calculable. SwiftUI tiene varios tipos de property wrappers ya implementados, pero nosotros siempre podemos añadir nuestros.

Los más utilizable son @State, @Binding. Son fundamentales para el desarrollo en Swift UI.

@State es probablemente el property wrapper más utilizado en SwiftUI. Es una variable local que se usa para vincular la vista y la variable. Siempre cuando se cambia el valor de variable @State, la vista vinculada se actualiza. Veamos un simple ejemplo.

struct ContentView : View {
    @State var myNumber = 1
    
    var body: some View {
            VStack(alignment: .center, spacing: 10.0) {
                Text("Number \(myNumber)")
                Button(action: { self.myNumber+=1}) {
                    Text("Add 1")
                        .foregroundColor(Color.red)
                }
        }
    }
}

Cada vez que pulsemos el botón el texto se actualiza, porque nuestra vista «Text» está vinculada con el valor de variable @State. Nuestra variable es «la fuente de la verdad» de nuestra vista.

@Binding

Como aprendimos anteriormente, los property wrappers siempre deben relacionarse con una vista específica. Pero a veces queremos tener acceso a un property wrapper desde el exterior, por ejemplo, desde las vistas secundarias.

Para crear dicha referencia, usamos el property wrapper @Binding. Si quieres crear un enlace desde una vista secundaria a la vista que contiene la propiedad State, simplemente declara una variable dentro de la vista secundaria y márcala con la palabra clave @Binding.

struct ChildView: View {
    
    @Binding var myBinding: String
    
    var body: some View {
        //...
    }
}

Luego puedes crear el enlace inicializando la vista secundaria con referencia a la variable @State utilizando la sintaxis «$».

struct ContentView: View {
    
    @State var myBinding = "Hello"
    
    var body: some View {
         ChildView(myBinding: $myBinding)
    }
    
}

La variable @Binding se usa igual que @State. Por ejemplo, cuando actualiza la variable @Binding, también hace que @State cambie su valor, lo que hace que la vista también vuelva a actualizarse.

struct ContentView: View {
    
    @State var myInteger = 1
    
    var body: some View {
        VStack {
            Text("\(myInteger)")
            OutsourcedButtonView(myInteger: $myInteger)
        }
    }
    
}

struct OutsourcedButtonView: View {
    
    @Binding var myInteger: Int
    
    var body: some View {
        Button(action: {self.myInteger += 1}) {
            Text("Tap me!")
        }
    }
}

En el ejemplo anterior, ContentView contiene una variable que contiene un número entero y un texto para mostrar esos datos. ChildView contiene un enlace a esta variable @State y un botón para aumentar el valor de la propiedad del enlace. Cuando tocamos el botón, los datos del estado se actualizan a través del enlace, lo que hace que ContentView se vuelva a mostrar y finalmente muestre el numero actualizado.

Hagamos un ejemplo que nos ayudará a entender cómo funciona el mecanismo de property wrappers.

struct ContentView : View {
    @State private var showingAlert = false
    @State private var name = "Anton"
    
    var body: some View {
        NavigationView {
                VStack(spacing: 20.0) {
                    ChildView(name: $name, showingAlert: $showingAlert)
                        }.alert(isPresented: $showingAlert) {
                            Alert(title: Text("Important message"), 
                            message: Text("Welcome \(name)"), 
                            dismissButton: .default(Text("Got it!")))
                        }
                        NavigationLink(destination: DetailView()) {
                            Text("Go to Detail")
                        }
                    }.padding(.all, 10.0)
                    .navigationBarTitle("Main Page")
        }
}

struct ChildView: View {
    @Binding var name : String
    @Binding var showingAlert: Bool

    var body: some View {
        VStack {
            TextField("Placeholder", text: $name)
                .padding(.horizontal, 15.0)
            Button(action: {
                self.showingAlert = true
            }) {
                Text("Show Alert")
                    .foregroundColor(Color.red)
            }
        }
    }
}

Aquí hemos dividido nuestra vista en dos, creando dos vistas: la parental y la filial. Creamos las variables «showingAlert» y «name» con el property wrapper @State y las pasamos a la vista filial, pero dentro de ella se necesita usar el property wrapper @Binding. Las propiedades @State son «locales» y las de @Binding se usan para vincular dos vistas diferentes. Para crear un vínculo (binding) entre el property wrapper y otra vista se utiliza el símbolo «$». Es como una referencia al valor de esta propiedad que crea un enlace (binding) para pasarlo a otras vistas.

Con la ayuda de los property wrappers podemos dividir nuestra lógica en diferentes partes. También nos ayuda a sincronizar las vistas con las variables de manera muy fácil y eficaz. No tenemos que escribir mucho código para vincular nuestro modelo con la interfaz. Los property wrappers son imprescindibles en el desarrollo Swift UI.

Conclusiones

Este artículo apenas roza la superficie de SwiftUI, pero ahora tienes una idea de cómo usar algunas nuevas herramientas de Xcode para diseñar y previsualizar vistas, y cómo usar las variables @State y @Binding para mantener actualizada su UI. También cómo presentar las alertas. Sin duda, con el paso del tiempo SwiftUI será el estandarte en el desarrollo para iOS y Mac. Solo puedes utilizar SwiftUI a partir de iOS 13 y MacOS X 10.15. Esto impide el uso de SwiftUI para crear las aplicaciones que ya existen, pero dentro de un par de años no será algo relevante. Así que tenemos el tiempo para aprender Swift UI hasta que se convierta en el estándar del mundo iOS.

 

Referencias:

  1. https://developer.apple.com/documentation/swiftui
  2. https://nshipster.com/propertywrapper/
  3. https://www.raywenderlich.com/3715234-swiftui-getting-started

1 COMENTARIO

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad