La traducción del artículo fue preparada en la víspera del lanzamiento del curso avanzado "Desarrollador iOS" .
Hola y bienvenidos a nuestro tutorial! En esta serie, hablamos sobre cómo navegar entre vistas en SwiftUI (¡sin usar una vista de navegación!). Aunque esta idea puede parecer trivial, pero después de comprenderla un poco más, podemos aprender mucho sobre los conceptos de flujo de datos utilizados en SwiftUI.En la parte anterior, aprendimos cómo implementar esto usando @ObservableObject
. En esta parte, veremos cómo hacer lo mismo, pero de manera más eficiente usando @EnvironmentObject. También vamos a agregar una pequeña animación de transición.Esto es lo que vamos a lograr:Que tenemos
Entonces, acabamos de descubrir cómo navegar entre diferentes vistas usando ObservableObject. En pocas palabras, creamos un ViewRouter y lo asociamos con la Vista Madre y nuestra Vista de Contenido. Luego, simplemente manipulamos la propiedad CurrentPage ViewRouter haciendo clic en los botones de Vista de contenido. Después de eso, MotherView se actualiza para mostrar la vista de contenido correspondiente.Pero hay una segunda forma más eficiente de lograr esta funcionalidad: @EnvironmentObject
¡ usar !Sugerencia: puede descargar los últimos desarrollos aquí (esta es la carpeta "NavigateInSwiftUIComplete") : GitHub¿Por qué el uso ObservableObject
no es la mejor solución?
Probablemente se esté preguntando: ¿por qué deberíamos implementar esto de otra manera, cuando ya tenemos una solución que funcione? Bueno, si observa la lógica de la jerarquía de nuestra aplicación, se le hará evidente. Nuestra MotherView es la vista raíz que inicializa la instancia de ViewRouter. En MotherView, también inicializamos ContentViewA y ContentViewB, pasándoles la instancia de ViewRouter como BindableObject.Como puede ver, debemos seguir una jerarquía estricta que pasa un ObservableObject inicializado en sentido descendente a todas las subvistas. Ahora esto no es tan importante, pero imagine una aplicación más compleja con muchas vistas. Siempre necesitamos realizar un seguimiento de la transmisión de la vista raíz inicializable Observable a todas las subvistas y todas las subvistas de subvistas, etc., lo que en última instancia puede convertirse en una tarea bastante tediosa.
Para resumir: usar clean ObservableObject
puede ser problemático cuando se trata de jerarquías de aplicaciones más complejas.En cambio, podríamos inicializar ViewRouter cuando se inicie la aplicación para que todas las vistas se puedan conectar directamente a esta instancia, o, más bien, verla, independientemente de la jerarquía de la aplicación. En este caso, la instancia de ViewRouter se verá como una nube que vuela sobre el código de nuestra aplicación, a la que todas las vistas acceden automáticamente sin preocuparse por la cadena de inicialización correcta en la jerarquía de vistas.Este es solo el trabajo EnvironmentObject
!¿Qué es EnvironmentObject
?
EnvironmentObject
Es un modelo de datos que, después de la inicialización, puede intercambiar datos con todas las representaciones de su aplicación. ¡Lo que es especialmente bueno es lo que se EnvironmentObject
crea al proporcionar ObservableObject
, para que podamos usar el nuestro ViewRouter
para crear EnvironmentObject
!Tan pronto como declaramos nuestro ViewRouter
como EnvironmentObject
, todas las vistas pueden ser conectados al mismo, así como a las normales ObservableObject
, pero sin la necesidad de una cadena de inicialización abajo de la jerarquía de la aplicación!Como ya se mencionó, EnvironmentObject
ya debe inicializarse la primera vez que se accede. Como la nuestra MotherView
, como vista raíz, analizará la propiedad CurrentPage
ViewRouter
, debemos inicializarla EnvironmentObject
cuando se inicie la aplicación. Entonces podemos cambiar automáticamente la página actualEnvironmentObject
de ContentView
, que luego requiere MotherView
una nueva representación.
Implementación ViewRouter
comoEnvironmentObject
Entonces, ¡actualice el código de nuestra aplicación!Primero, cambie el contenedor de la propiedad viewRouter
dentro MotherView
de @ObservableObject
a @EnvironmentObject
.import SwiftUI
struct MotherView : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
}
}
La propiedad viewRouter
ahora está buscando ViewRouter-EnvironmentObject
. Por lo tanto, debemos proporcionar a nuestra estructura MotherView_Previews
la instancia adecuada:#if DEBUG
struct MotherView_Previews : PreviewProvider {
static var previews: some View {
MotherView().environmentObject(ViewRouter())
}
}
#endif
Como se mencionó anteriormente, al iniciar nuestra aplicación, se le debe proporcionar inmediatamente una instancia ViewRouter
de calidad EnvironmentObject
, ya que MotherView
ahora se conoce como la representación raíz EnvironmentObject
. Por lo tanto, actualice la función de escena dentro del archivo de la SceneDelegage.swift
siguiente manera:func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: MotherView().environmentObject(ViewRouter()))
self.window = window
window.makeKeyAndVisible()
}
}
Genial, ahora cuando inicia la aplicación, SwiftUI crea una instancia ViewRouter
en calidad EnvironmentObject
, a la que ahora se pueden adjuntar todas las vistas de nuestra aplicación.A continuación, vamos a actualizar la nuestra ContentViewA
. Cambie su viewRouter
propiedad a EnvironmentObject
y también actualice la estructura ContentViewA_Previews
.import SwiftUI
struct ContentViewA : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
}
}
#if DEBUG
struct ContentViewA_Previews : PreviewProvider {
static var previews: some View {
ContentViewA().environmentObject(ViewRouter())
}
}
#endif
Sugerencia: De nuevo, la estructura ContentViewsA_Previews
tiene su propia instancia ViewRouter
, ¡pero ContentViewA
está asociada con la instancia creada cuando se lanzó la aplicación!Repitamos esto para ContentViewB
:import SwiftUI
struct ContentViewB : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
}
}
#if DEBUG
struct ContentViewB_Previews : PreviewProvider {
static var previews: some View {
ContentViewB().environmentObject(ViewRouter())
}
}
#endif
Dado que viewRouter
nuestras propiedades ContentView
ahora están directamente relacionadas con / observar la instancia inicial ViewRouter
como EnvironmentObject
, ya no necesitamos inicializarlas en la nuestra MotherView
. Entonces, vamos a actualizar nuestro MotherView
:struct MotherView : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
VStack {
if viewRouter.currentPage == "page1" {
ContentViewA()
} else if viewRouter.currentPage == "page2" {
ContentViewB()
}
}
}
}
Esto es simplemente genial: ya no necesitamos inicializar ViewRouter
dentro de la nuestra MotherView
y pasar su instancia a ContentView, que puede ser muy eficiente, especialmente para jerarquías más complejas.Genial, ejecutemos nuestra aplicación y veamos cómo funciona. ¡Genial, todavía podemos movernos entre nuestros puntos de vista!Agregar animaciones de transición
Como beneficio adicional, veamos cómo agregar una animación de transición al cambiar de “página1” a “página2”.En SwiftUI, esto es bastante simple.Mire el willChange
método que llamamos el archivo ViewRouter.swift
cuando se CurrentPage
actualiza. Como ya sabe, esto provoca un enlace MotherView
para volver a mostrar su cuerpo, en última instancia, muestra otro ContentView
, lo que significa pasar a otro ContentView
. Podemos agregar animación simplemente envolviendo el método willChange
en una función withAnimation
:var currentPage: String = "page1" {
didSet {
withAnimation() {
willChange.send(self)
}
}
}
Ahora podemos agregar una animación de transición al mostrar otra Content View
."WithAnimation (_: _ :): devuelve el resultado de volver a calcular el cuerpo de la vista con la animación proporcionada"
Apple
Queremos proporcionar a nuestra aplicación una transición emergente cuando navegue de ContentViewA
a ContentViewB
. Para hacer esto, vaya al archivo MotherView.swift
y agregue el modificador de transición cuando se le llame ContentViewB
. Puede elegir uno de varios tipos de transición predefinidos o incluso crear uno propio (pero este es un tema para otro artículo). Para agregar una transición emergente, seleccionamos un tipo .scale
.var body: some View {
VStack {
if viewRouter.currentPage == "page1" {
ContentViewA()
} else if viewRouter.currentPage == "page2" {
ContentViewB()
.transition(.scale)
}
}
}
Para ver cómo funciona, ejecute su aplicación en un simulador normal: ¡Genial, agregamos una agradable animación de transición a nuestra aplicación en solo unas pocas líneas de código!¡Puedes descargar todo el código fuente aquí !Conclusión
¡Eso es todo! Aprendimos por qué es mejor usarlo EnvironmentObject
para navegar entre vistas en SwiftUI y cómo implementarlo. También aprendimos cómo agregar animaciones de transición a la navegación. Si quieres saber más, síguenos en Instagram y suscríbete a nuestro boletín para no perderte actualizaciones, tutoriales y consejos sobre SwiftUI y mucho más.
Lección gratuita: "Aceleración de aplicaciones iOS con instrumentos"