
Hola Mi nombre es Renat, estoy desarrollando un servicio de análisis de suscripción en iOS: Apphud.
Como saben, Apple en WWDC 2019 presentó su nuevo marco SwiftUI, que está diseñado en el futuro para reemplazar (¿o no?) El familiar UIKit. SwiftUI le permite describir la interfaz de la aplicación en un estilo declarativo y reduce en gran medida la cantidad de código.
Apple ya ha introducido algunos tutoriales interesantes en inglés con muchos ejemplos. Trataré de hablar sobre el nuevo marco en forma de preguntas y respuestas. Entonces vamos.
Antes de empezar
Para trabajar con SwiftUI, debe descargar Xcode 11 Beta . También debe ser un desarrollador registrado de Apple. Tener la última versión de macOS Catalina es deseable, pero no necesario. Sin ella, Canvas no estará disponible.
Por lo tanto, en Xcode 11 Beta, cree un nuevo proyecto y asegúrese de que "Usar SwiftUI" esté marcado.
Preguntas y respuestas
¿A dónde fue Interface Builder?
SwiftUI ya no necesita Interface Builder : ha sido reemplazado por Canvas , un editor de interfaz interactivo que está estrechamente relacionado con el código. Al escribir código, su parte visual en el lienzo se genera automáticamente y viceversa. Muy conveniente y lo más importante seguro. Ahora su aplicación no se @IBOutlet
debido al hecho de que olvidó actualizar la @IBOutlet
con la variable. En este artículo no tocaremos el lienzo , consideraremos solo el código.
¿Ha cambiado el inicio de la aplicación?
Sí, ahora el objeto inicial en la interfaz de la aplicación no es UIWindow
, sino la nueva clase UIScene
(o su descendiente UIWindowScene
). Y se agrega una ventana a la escena. Estos cambios afectan no solo a SwiftUI, sino a iOS 13 en su conjunto.
Cuando cree un proyecto, verá los archivos AppDelegate , SceneDelegate y ContentView . SceneDelegate : un delegado de la clase UIWindowScene
, utilizado para controlar escenas en la aplicación. Muy reminiscente de AppDelegate .

La clase SceneDelegate se especifica en Info.plist
En el método delegado, scene: willConnectTo: options:
crea una ventana y un UIHostingController
raíz que contiene un ContentView
. ContentView
es nuestra página de "inicio". Todo el desarrollo se llevará a cabo en esta clase.
¿En qué se diferencia View de UIView?
ContentView.swift
abra ContentView.swift
, verá una declaración de contenedor de ContentView. Como ya entendió, no hay viewDidLoad
familiares de viewDidLoad
o viewDidAppear
viewDidLoad
viewDidAppear
. La base de las pantallas aquí no es el UIViewController
, sino la Vista . Lo primero que debe notar es que ContentView
es una struct
que acepta el protocolo View
. Sí, View
convertido en un protocolo, y muy simple. El único método que necesita implementar en ContentView
es describir el body
la variable. Todas sus subvistas y vistas personalizadas deben aceptar el protocolo de View
, es decir, deben tener una variable de body
.
¿Qué es el cuerpo?
Body
es directamente nuestro contenedor donde se agregan todas las demás subvistas . Esto es algo similar al cuerpo en una página html , donde la página html es ContentView
. Body
siempre debe tener exactamente un descendiente y cualquier clase que acepte el protocolo View
.
struct ContentView: View { var body: some View { Text("Hello, world!") } }
¿Tipos de retorno opacos o cuáles son algunos?
La construcción de some TypeName
es una innovación de Swift 5.1 llamada tipo de retorno opaco . Se utiliza para casos en los que no es importante para nosotros qué objeto devolver, lo principal es que admite el tipo especificado, en este caso, el protocolo de visualización.
Si simplemente escribiéramos var body: View
, esto significaría que deberíamos devolver la View
. La clase Any
tampoco funciona, ya que tendríamos que realizar una operación de conversión de tipo ( utilizando el operador as!
). Por lo tanto, se les ocurrió la palabra especial some
delante del nombre del protocolo para indicar el tipo de retorno opaco . En lugar de View
podemos devolver Text
, Image
, VStack
, cualquier cosa, ya que todos admiten el protocolo View
. Pero debe haber exactamente un elemento: al intentar devolver más de una View
compilador arrojará un error.

Error de compilación al intentar devolver más de un elemento al cuerpo
¿Cuál es la sintaxis dentro de los corchetes y dónde está addSubview?
Swift 5.1 introdujo la capacidad de agrupar objetos en un solo conjunto en un estilo declarativo. Esto es similar a una matriz dentro de un bloque de cierre , pero los elementos se enumeran desde una nueva línea sin comas y sin retorno . Este mecanismo se llamaba Function Builder .
Esto es ampliamente utilizado en SwiftUI. Basado en Function Builder, ViewBuilder
, un diseñador de interfaz declarativo. Con ViewBuilder
ya no necesitamos escribir addSubview
para cada elemento, simplemente enumere todas las View
desde una nueva línea dentro del bloque de cierre . SwiftUI agregará y agrupará elementos en un contenedor principal más complejo.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @_functionBuilder public struct ViewBuilder {
Anuncio de ViewBuilder en el marco de SwiftUI
¿Cómo agregar UILabel, UIImageView y otros elementos?
Los elementos se crean de manera muy simple: cada View
debe escribirse desde una nueva línea y cambiar la apariencia utilizando las funciones modificadoras ( modificadores de vista ). La diferencia entre modificadores y funciones que nos son familiares es que siempre devuelven un objeto contenedor en lugar de void
. Por lo tanto, podemos crear cadenas enteras de modificadores a través del punto.
var body: some View { VStack{ Text("World Time").font(.system(size: 30)) Text("Yet another subtitle").font(.system(size: 20)) } }
Sin embargo, no todos los controles y View tienen sus análogos en SwiftUI. Aquí hay una lista parcial de clases de UIKit y sus análogos:
UITableView
-> List
UICollectionView
no tiene análogo
UILabel
-> Text
UITextField
-> TextField
UIImageView
-> Image
UINavigationController
-> NavigationView
UIButton
-> Button
UIStackView
-> HStack
/ VStack
UISwitch
-> Toggle
UISlider
-> Slider
UITextView
no tiene análogo
UIAlertController
-> Alert
/ ActionSheet
UISegmentedControl
-> UISegmentedControl
UIStepper
-> Stepper
UIDatePicker
-> DatePicker
¿Cómo es la navegación entre las pantallas?
El controlador de navegación asume el papel de un NavigationView
especial. Simplemente envuelva su código en NavigationView{}
. Y la acción de transición en sí se puede agregar a un botón especial NavigationLink
, que empuja la pantalla condicional DetailView
.
var body: some View { NavigationView { Text("World Time").font(.system(size: 30)) NavigationLink(destination: DetailView() { Text("Go Detail") } } }
¿Cómo presentar nuevas vistas modalmente? Esto se hace, por ejemplo, usando la construcción de hoja :
Button(action: { print("Button Pushed") self.show_modal = true }) { Text("Present Modal") }.sheet(isPresented: self.$show_modal) { ModalView() }
Como se mencionó anteriormente, el body
puede devolver no solo una instancia de View
, sino también cualquier otra clase que acepte este protocolo. ¡Esto nos da la oportunidad de DetailView
no DetailView
, sino incluso Text
o Image
!
¿Cómo organizar los elementos en la pantalla?
Los elementos están dispuestos independientemente uno del otro y pueden ubicarse verticalmente dentro de VStack
, horizontalmente HStack
y uno encima de otro ZStack
. ScrollView
y ListView
también están disponibles para nosotros. Puede alternar y compartir estos contenedores para obtener cualquier malla de elementos.
Al combinar contenedores entre sí, puede obtener un árbol bastante grande con una gran cantidad de archivos adjuntos. Sin embargo, SwiftUI está optimizado específicamente para esto, por lo que la anidación profunda de contenedores no afecta el rendimiento. Esto se afirma en el video con wwdc (a partir de las 15:32 ).
var body: some View { NavigationView { VStack { NavigationLink(destination: LargeView(timeString: subtitle)) { Text("See Fullscreen") } Text("World Time").font(.system(size: 30)) } } }
¿Cómo mostrar la barra de navegación?
Declarar una NavigationView
no NavigationView
suficiente; debe especificar un título y estilo de navegación para la barra de navegación .
NavigationView { VStack{}.navigationBarTitle(Text("World Time"), displayMode: .inline) }
Tenga en cuenta que la función navigationBarTitle
no se llama en NavigationView
, sino en su View
interna. DisplayMode es un parámetro que indica el estilo de la barra de navegación : grande o estándar.
¿Existe un análogo del método viewDidLoad?
Si desea ejecutar código al inicializar la View
, puede hacerlo agregando la función onAppear {}. OnAppear se puede agregar a cualquier View
, por ejemplo, a VStack
. En este ejemplo, cuando el contenedor aparece en la pantalla, se realiza una solicitud http al servidor.
struct ContentView : View { @State var statusString : String = "World Time" var body: some View { NavigationView { VStack { NavigationLink(destination:DetailView()) { Text("Go Detail") } Text(statusString).font(.system(size: 30)) }.onAppear { self.loadTime() } } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date())" } } } }
Llamamos a la función loadTime
, que solicita la hora actual del servidor y devuelve el modelo WorldTime
. No iremos en ciclos en la clase NetworkService
, puede ver todo el código, una vez que haya descargado los códigos fuente. Enlace al final del artículo.
La variable var statusString
se var statusString
para asignarle la hora actual más tarde. La variable tiene un atributo especial @State
. Que quiere decir?
¿ @State
propiedades o qué es @State
?
Swift 5.1 introdujo los llamados envoltorios de propiedades (o delegados de propiedades ). En SwiftUI, los contenedores de propiedades se utilizan para actualizar o enlazar uno de los parámetros de vista con nuestra propia variable, por ejemplo, el valor del interruptor de alternancia .
El atributo @State
es un atributo especial que se coloca antes de una declaración de variable. Esto nos permite rastrear automáticamente los cambios de propiedad sin código adicional. En el ejemplo anterior, el texto "Hora mundial" cambiará a la fecha actual tan pronto como actualicemos el valor de statusString.
Para enlazar valores ( Enlace de propiedades ), podemos especificar un carácter especial $
antes del nombre de la variable en el código:
struct DetailsView: View { @State var changeToggle: Bool var body: some View { Toggle(isOn: $changeToggle) { Text("Change Toggle") } } }
Al cambiar la posición del interruptor, el valor de la variable también cambiará.
Los envoltorios de propiedades son un componente muy importante de SwiftUI, solo los mencioné de pasada. Para conocer más detalladamente los envoltorios de propiedades, mire el video de wwdc aquí (desde el minuto 37), aquí (desde el minuto 12) y aquí (desde el minuto 19).
¿Cómo agregar una vista al tiempo de ejecución?
Vale la pena señalar de inmediato que no puede agregar la vista en ningún momento en el sentido literal de la palabra. SwiftUI es un marco declarativo que representa la vista completa. Sin embargo, puede establecer varias condiciones dentro del cuerpo y actualizar el estado de la vista cuando cambian. En este ejemplo, usamos el grupo más simple de @State – if
con la variable isTimeLoaded
.
struct ContentView : View { @State var statusString : String = "World Time" @State var isTimeLoaded : Bool = false var body: some View { NavigationView { VStack { if isTimeLoaded { addNavigationLink() } Text(statusString).font(.system(size: 30)).lineLimit(nil) }.navigationBarTitle(Text("World Time"), displayMode: .inline) }.onAppear { self.loadTime() } } func addNavigationLink() -> some View { NavigationLink(destination: Text("124!!!")) { Text("Go Detail") } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date().description(with: Locale.current))" self.isTimeLoaded = true } } } } struct DetailView : View { var timeString : String var body : some View { Text(timeString).font(.system(size: 40)).lineLimit(nil) } }
Por cierto, ¿notó que la función addNavigationLink()
no tiene la palabra return
? Esta es otra innovación de Swift 5.1: para funciones con una sola expresión, ahora es opcional escribir return
. Pero puedes escribir.
Conclusión
Esto es solo parte de las preguntas y respuestas de SwiftUI. Examiné cuestiones generales, espero que este artículo ayude a los principiantes a comprender los puntos principales de este marco. SwiftUI sigue sin procesar, pero sin duda se mejorará.
La pregunta lógica es: ¿ vale la pena aprender UIKit? Por supuesto que si . UIKit es la base de la programación en iOS y se desarrollará aún más. Además, muchos componentes SwiftUI son una envoltura sobre UIKit. Bueno, hasta ahora no hay bibliotecas, frameworks, pods para SwiftUI. Todo tendrá que ser escrito por ti mismo. Por lo tanto, es mejor estudiar ambos enfoques de desarrollo, por lo que será un desarrollador más valioso.
Puede descargar las fuentes del proyecto aquí .
Gracias por leer el artículo hasta el final. Espero que les sea útil.
Que leer