NavigationStack é um contêiner de navegação moderno em SwiftUI, apresentado no iOS 16+ e substituindo o NavigationView. De acordo com a Documentação do Desenvolvedor Apple, 2024, o NavigationStack gerencia uma pilha de telas através de um caminho de navegação type-safe (NavigationPath), suporta navegação profunda, retorno programático à tela raiz e preservação de estado quando os dados mudam. Ao contrário do NavigationView, o NavigationStack não requer encapsulamento em um contêiner adicional e fornece um Binding direto ao caminho de navegação.
Pontos principais
NavigationStack é uma View contêiner que implementa navegação baseada em pilha (LIFO). Ela gerencia o histórico de transições, permitindo colocar novas telas na pilha e voltar através do botão “Voltar” do sistema ou programaticamente. NavigationStack faz parte do SwiftUI a partir do iOS 16, iPadOS 16, macOS 13, watchOS 9 e tvOS 16.
A principal inovação do NavigationStack é o caminho de navegação type-safe. Em vez de especificar diretamente um destino ao criar um NavigationLink, você coloca um valor no caminho, e a View de destino é registrada separadamente através do modificador .navigationDestination(for:destination:). Isso separa a navegação da renderização, tornando o código mais modular e testável.
De acordo com a WWDC 2022 (Sessão 10054), o NavigationStack usa um novo mecanismo de navegação baseado em ObservableObject e no ciclo de vida do SwiftUI. Ao contrário do NavigationView, que dependia do UINavigationController internamente, o NavigationStack é totalmente implementado em SwiftUI, melhorando a previsibilidade e a compatibilidade com o ciclo de vida do SwiftUI.
NavigationStack aceita uma View raiz e um caminho de navegação opcional (Binding para NavigationPath ou um array de valores Hashable). Todas as telas filhas são colocadas na pilha através de NavigationLink ou adicionando valores programaticamente ao caminho.
NavigationView era o principal contêiner de navegação em SwiftUI antes do iOS 16. Ele gerenciava automaticamente o UINavigationController internamente, o que levava a vários problemas: comportamento imprevisível ao alterar dados, complexidade com navegação programática e falta de type safety.
| Característica | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| Tipo de navegação | Pilha (LIFO) | Pilha (LIFO) |
| Caminho de navegação | Tipado (NavigationPath) | Não suportado |
| Deep linking | Suporte integrado | Requer soluções |
| Retorno programático | Via caminho (pop, popToRoot) | dismiss, presentationMode |
| Internamente | SwiftUI nativo | UINavigationController |
| Compatibilidade | iOS 16+ | iOS 13+ |
Vantagem principal do NavigationStack — navegação type-safe. Você define o caminho como um array de tipos específicos (ou NavigationPath para pilhas heterogêneas) e registra um destino para cada tipo. Isso elimina erros de incompatibilidade de tipos e torna a navegação previsível.
NavigationView está obsoleto no iOS 17. A Apple recomenda migrar para NavigationStack em todos os novos projetos e ao atualizar a versão mínima para iOS 16.
NavigationPath é um tipo que representa o caminho de navegação no NavigationStack. Ele pode armazenar valores heterogêneos (AnyHashable) ou ser usado com um tipo específico via Binding a um array [T: Hashable]. NavigationPath codifica e decodifica automaticamente para preservação de estado.
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: String.self) { value in
DetailView(id: value)
}
.navigationDestination(for: Int.self) { value in
NumberView(number: value)
}
}
}
func goToRoot() {
path.removeLast(path.count)
}
func pushDeepLink() {
path.append("detail_42")
}
}
Pilha heterogênea: NavigationPath pode conter valores de diferentes tipos se eles implementarem Hashable. Por exemplo, a primeira tela pode aceitar uma String (ID), a segunda um Int (número), a terceira um enum Route personalizado. Cada tipo registra um .navigationDestination separado para exibição.
Suporte Codable: NavigationPath implementa Codable se todos os valores no caminho também forem Codable + Hashable. Isso permite salvar e restaurar o estado de navegação ao reiniciar o aplicativo ou ao entrar em segundo plano.
.navigationDestination(for:destination:) — um modificador que registra uma View de destino para um tipo de dados específico. Quando NavigationLink coloca um valor desse tipo no caminho, SwiftUI encontra automaticamente o .navigationDestination correspondente e cria a tela.
enum AppRoute: Hashable {
case profile(UserID)
case settings
case about
}
struct AppNavigation: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile(let id):
ProfileView(userId: id)
case .settings:
SettingsView()
case .about:
AboutView()
}
}
}
}
}
// Navigate: path.append(AppRoute.profile("user_123"))
Regra importante: .navigationDestination deve ser aplicado a uma View que esteja dentro do NavigationStack, e antes que NavigationLink coloque um valor no caminho. Geralmente é adicionado à View raiz ou a um contêiner de seção. Se nenhum .navigationDestination for encontrado para o tipo do valor, a transição não ocorrerá.
De acordo com a SwiftUI Engineering (2023), .navigationDestination pode ser registrado em diferentes níveis da hierarquia. SwiftUI procura o destino mais próximo durante a navegação. Isso permite substituir o destino para um tipo em diferentes partes do aplicativo.
Padrão 1: navegação via enum Route. Defina um enum com valores associados para todas as telas do aplicativo. Use um .navigationDestination para AppRoute e um switch para roteamento. Isso fornece uma única fonte de verdade para todas as transições possíveis no aplicativo.
struct StoreView: View {
@State private var path: [ProductRoute] = []
var body: some View {
NavigationStack(path: $path) {
ProductGrid()
.navigationDestination(for: ProductRoute.self) { route in
switch route {
case .detail(let product):
ProductDetail(product: product)
case .reviews(let productId):
ReviewsView(productId: productId)
}
}
}
}
}
enum ProductRoute: Hashable {
case detail(Product)
case reviews(String)
}
Padrão 2: navegação programática e deep linking. NavigationStack permite gerenciamento programático da pilha: adicionar, remover telas e retornar à raiz. Isso é necessário para notificações push, deep links e restauração da navegação após reinicialização.
Padrão 3: array de tipos em vez de NavigationPath. Se todas as telas usam o mesmo tipo (por exemplo, String ou um enum personalizado), use Binding para [T]. Isso fornece tipagem mais forte e melhor desempenho que NavigationPath. NavigationPath é justificado para pilhas heterogêneas com diferentes tipos de tela.
De acordo com a Point-Free (2024), NavigationStack com enum Route é a maneira preferida de organizar a navegação em aplicativos SwiftUI. Torna todas as transições possíveis explícitas, type-safe e testáveis, o que é especialmente importante para grandes projetos com dezenas de telas.
Perguntas frequentes
NavigationStack — um contêiner de navegação SwiftUI (iOS 16+) que gerencia uma pilha de telas através de um caminho type-safe. Substituiu o NavigationView, oferecendo suporte a deep linking, navegação programática e preservação de estado.
NavigationStack usa um caminho type-safe (NavigationPath) em vez de vincular diretamente NavigationLink a um destino. Suporta navegação programática, deep linking e Codable para preservação de estado. Funciona em SwiftUI, não através de UINavigationController.
NavigationPath é um tipo que representa uma sequência de telas na pilha. Você adiciona valores ao caminho via path.append() ou NavigationLink com value. Cada tipo registra um .navigationDestination para exibição. NavigationPath suporta Codable e preservação automática de estado.
Através do gerenciamento programático do caminho: após processar a URL, chame path.append() com o valor de rota correspondente. NavigationStack exibe automaticamente a tela de destino. Retorno à raiz — path.removeLast(path.count).
Sim, se sua versão mínima for iOS 16+. NavigationView está obsoleto no iOS 17. A migração fornece navegação type-safe, suporte a deep linking e melhor alinhamento com o ciclo de vida do SwiftUI. Para projetos com iOS 15 e inferior, continue usando NavigationView por enquanto.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também