A tradução do artigo foi preparada às vésperas do lançamento do curso avançado "iOS Developer" .
Olá e bem-vindo ao nosso tutorial! Nesta série, falamos sobre como navegar entre visualizações no SwiftUI (sem usar uma visualização de navegação!). Embora essa ideia possa parecer trivial, mas entendendo-a um pouco mais profundamente, podemos aprender muito sobre os conceitos de fluxo de dados usados no SwiftUI.Na parte anterior, aprendemos como implementar isso usando @ObservableObject
. Nesta parte, veremos como fazer o mesmo, mas com mais eficiência usando o @EnvironmentObject. Também vamos adicionar uma pequena animação de transição.Aqui está o que vamos alcançar:O que nós temos
Então, acabamos de descobrir como navegar entre diferentes visualizações usando o ObservableObject. Em poucas palavras, criamos um ViewRouter e o associamos à Mother View e à Content View. Em seguida, simplesmente manipulamos a propriedade CurrentPage ViewRouter clicando nos botões Exibição de conteúdo. Depois disso, o MotherView é atualizado para exibir a Visualização de Conteúdo correspondente!Mas existe uma segunda maneira, mais eficiente, de obter essa funcionalidade: use @EnvironmentObject
!Dica: você pode baixar os desenvolvimentos mais recentes aqui (esta é a pasta "NavigateInSwiftUIComplete") : GitHubPor que o uso ObservableObject
não é a melhor solução
Você provavelmente está se perguntando: por que devemos implementar isso de outra maneira, quando já temos uma solução funcional? Bem, se você olhar para a lógica da hierarquia de nosso aplicativo, ela ficará clara para você. Nosso MotherView é a visualização raiz que inicializa a instância do ViewRouter. No MotherView, também inicializamos o ContentViewA e o ContentViewB, passando a instância ViewRouter como um BindableObject.Como você pode ver, devemos seguir uma hierarquia estrita que passa um ObservableObject inicializado para todas as subvisões. Agora, isso não é tão importante, mas imagine uma aplicação mais complexa com muitas visualizações. Sempre precisamos acompanhar a transmissão da visão raiz observável inicializada para todas as sub-visões e todas as sub-visões de sub-visões, etc., o que pode se tornar uma tarefa bastante tediosa.
Resumindo: o uso de limpeza ObservableObject
pode ser problemático quando se trata de hierarquias de aplicativos mais complexas.Em vez disso, poderíamos inicializar o ViewRouter quando o aplicativo iniciar, para que todas as visualizações possam ser conectadas diretamente a essa instância, ou melhor, assisti-la, independentemente da hierarquia do aplicativo. Nesse caso, a instância do ViewRouter parecerá uma nuvem que sobrevoa o código do nosso aplicativo, ao qual todas as visualizações acessam automaticamente sem se preocupar com a cadeia de inicialização correta na hierarquia da visualização.Este é apenas o trabalho EnvironmentObject
!O que é EnvironmentObject
?
EnvironmentObject
É um modelo de dados que, após a inicialização, pode trocar dados com todas as representações do seu aplicativo. O que é especialmente bom é o que é EnvironmentObject
criado pelo fornecimento ObservableObject
, para que possamos usar o nosso ViewRouter
para criar EnvironmentObject
!Assim que declarou a nossa ViewRouter
como EnvironmentObject
, todas as representações podem ser anexado a ele da mesma forma que os normais ObservableObject
, mas sem a necessidade de uma cadeia de inicializações para baixo na hierarquia aplicação!Como já mencionado, ele EnvironmentObject
já deve ser inicializado na primeira vez em que é acessado. Como a nossa MotherView
, como visualização raiz, examinará a propriedade CurrentPage
ViewRouter
, precisamos inicializá-la EnvironmentObject
quando o aplicativo for iniciado. Então podemos mudar automaticamente currentPageEnvironmentObject
de ContentView
, que então requer MotherView
nova renderização.
Implementação ViewRouter
comoEnvironmentObject
Então, vamos atualizar o código do nosso aplicativo!Primeiro, altere o invólucro da propriedade viewRouter
dentro MotherView
de de @ObservableObject
para @EnvironmentObject
.import SwiftUI
struct MotherView : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
}
}
A propriedade está viewRouter
olhando agora ViewRouter-EnvironmentObject
. Portanto, precisamos fornecer à nossa estrutura MotherView_Previews
a instância apropriada:#if DEBUG
struct MotherView_Previews : PreviewProvider {
static var previews: some View {
MotherView().environmentObject(ViewRouter())
}
}
#endif
Como mencionado acima - ao iniciar nosso aplicativo, ele deve ser imediatamente fornecido com uma instância ViewRouter
em qualidade EnvironmentObject
, já que MotherView
agora se refere à representação raiz EnvironmentObject
. Portanto, atualize a função de cena dentro do arquivo da SceneDelegage.swift
seguinte maneira: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()
}
}
Ótimo, agora quando você inicia o aplicativo, o SwiftUI cria uma instância ViewRouter
em qualidade EnvironmentObject
, à qual todas as visualizações do nosso aplicativo agora podem ser anexadas.Em seguida, vamos atualizar o nosso ContentViewA
. Altere sua viewRouter
propriedade para EnvironmentObject
e também atualize a estrutura 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
Dica: Novamente, a estrutura ContentViewsA_Previews
possui sua própria instância ViewRouter
, mas ContentViewA
está associada à instância criada quando o aplicativo foi iniciado!Vamos repetir isso 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
Como viewRouter
nossas propriedades ContentView
agora estão diretamente relacionadas / observam a instância inicial ViewRouter
como EnvironmentObject
, não precisamos mais inicializá-las na nossa MotherView
. Então, vamos atualizar nosso MotherView
:struct MotherView : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
VStack {
if viewRouter.currentPage == "page1" {
ContentViewA()
} else if viewRouter.currentPage == "page2" {
ContentViewB()
}
}
}
}
Isso é ótimo: não precisamos mais inicializar ViewRouter
dentro da nossa MotherView
e passar sua instância para o ContentView, que pode ser muito eficiente, especialmente para hierarquias mais complexas.Ótimo, vamos executar nosso aplicativo e ver como ele funciona. Ótimo, ainda podemos nos mover entre nossos pontos de vista!Adicionando animações de transição
Como um bônus, vamos ver como adicionar uma animação de transição ao alternar de "página1" para "página2".No SwiftUI, isso é bastante simples.Veja o willChange
método que chamamos de arquivo ViewRouter.swift
quando CurrentPage
atualizado. Como você já sabe, isso faz com que um vínculo MotherView
exiba novamente seu corpo, mostrando outro ContentView
, o que significa mudar para outro ContentView
. Podemos adicionar animação simplesmente envolvendo o método willChange
em uma função withAnimation
:var currentPage: String = "page1" {
didSet {
withAnimation() {
willChange.send(self)
}
}
}
Agora podemos adicionar uma animação de transição ao exibir outra Content View
.“WithAnimation (_: _ :) - retorna o resultado de recalcular o corpo da exibição com a animação fornecida”
Apple
Queremos fornecer ao nosso aplicativo uma transição pop-up ao navegar de ContentViewA
para ContentViewB
. Para fazer isso, vá para o arquivo MotherView.swift
e adicione o modificador de transição quando chamado ContentViewB
. Você pode escolher um dos vários tipos de transição predefinidos ou até criar o seu próprio (mas este é um tópico para outro artigo). Para adicionar uma transição pop-up, selecionamos um tipo .scale
.var body: some View {
VStack {
if viewRouter.currentPage == "page1" {
ContentViewA()
} else if viewRouter.currentPage == "page2" {
ContentViewB()
.transition(.scale)
}
}
}
Para ver como ele funciona, execute seu aplicativo em um simulador regular: Legal, adicionamos uma boa animação de transição ao nosso aplicativo em apenas algumas linhas de código!Você pode baixar todo o código fonte aqui !Conclusão
Isso é tudo! Aprendemos por que é melhor usar EnvironmentObject
para navegar entre visualizações no SwiftUI e como implementá-lo. Também aprendemos como adicionar animações de transição à navegação. Se você quiser saber mais, siga-nos no Instagram e assine a nossa newsletter para não perder atualizações, tutoriais e dicas sobre o SwiftUI e muito mais!
Lição gratuita: “Acelerando aplicativos iOS com instrumentos”