NavigationStack è un contenitore di navigazione moderno in SwiftUI, introdotto in iOS 16+ e che sostituisce NavigationView. Secondo la Documentazione per sviluppatori Apple, 2024, NavigationStack gestisce una pila di schermi attraverso un percorso di navigazione type-safe (NavigationPath), supporta la navigazione profonda, il ritorno programmatico allo schermo principale e la preservazione dello stato quando i dati cambiano. A differenza di NavigationView, NavigationStack non richiede l'incapsulamento in un contenitore aggiuntivo e fornisce un Binding diretto al percorso di navigazione.
Punti chiave
NavigationStack è una vista contenitore che implementa la navigazione basata su pila (LIFO). Gestisce la cronologia delle transizioni, consentendo di inserire nuovi schermi nella pila e tornare indietro tramite il pulsante “Indietro” di sistema o programmaticamente. NavigationStack fa parte di SwiftUI a partire da iOS 16, iPadOS 16, macOS 13, watchOS 9 e tvOS 16.
La principale innovazione di NavigationStack è il percorso di navigazione type-safe. Invece di specificare direttamente una destinazione quando si crea un NavigationLink, si inserisce un valore nel percorso e la vista di destinazione viene registrata separatamente tramite il modificatore .navigationDestination(for:destination:). Questo separa la navigazione dal rendering, rendendo il codice più modulare e testabile.
Secondo WWDC 2022 (Sessione 10054), NavigationStack utilizza un nuovo meccanismo di navigazione basato su ObservableObject e il ciclo di vita di SwiftUI. A differenza di NavigationView, che si affidava internamente a UINavigationController, NavigationStack è completamente implementato in SwiftUI, migliorando la prevedibilità e la compatibilità con il ciclo di vita di SwiftUI.
NavigationStack accetta una vista radice e un percorso di navigazione opzionale (Binding a NavigationPath o un array di valori Hashable). Tutti gli schermi figli vengono inseriti nella pila tramite NavigationLink o aggiungendo valori programmaticamente al percorso.
NavigationView era il contenitore di navigazione principale in SwiftUI prima di iOS 16. Gestiva automaticamente UINavigationController internamente, il che portava a diversi problemi: comportamento imprevedibile quando i dati cambiavano, complessità con la navigazione programmatica e mancanza di type safety.
| Caratteristica | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| Tipo di navigazione | Pila (LIFO) | Pila (LIFO) |
| Percorso di navigazione | Tipizzato (NavigationPath) | Non supportato |
| Deep linking | Supporto integrato | Richiede soluzioni |
| Ritorno programmatico | Tramite percorso (pop, popToRoot) | dismiss, presentationMode |
| Internamente | SwiftUI nativo | UINavigationController |
| Compatibilità | iOS 16+ | iOS 13+ |
Vantaggio chiave di NavigationStack — navigazione type-safe. Si definisce il percorso come un array di tipi specifici (o NavigationPath per pile eterogenee) e si registra una destinazione per ogni tipo. Questo elimina gli errori di disallineamento dei tipi e rende la navigazione prevedibile.
NavigationView è deprecato in iOS 17. Apple raccomanda di migrare a NavigationStack per tutti i nuovi progetti e quando si aggiorna la versione minima a iOS 16.
NavigationPath è un tipo che rappresenta il percorso di navigazione in NavigationStack. Può memorizzare valori eterogenei (AnyHashable) o essere utilizzato con un tipo specifico tramite Binding a un array [T: Hashable]. NavigationPath codifica e decodifica automaticamente per la preservazione dello stato.
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")
}
}
Pila eterogenea: NavigationPath può contenere valori di diversi tipi se implementano Hashable. Ad esempio, il primo schermo può accettare un String (ID), il secondo un Int (numero), il terzo un enum Route personalizzato. Ogni tipo registra un .navigationDestination separato per la visualizzazione.
Supporto Codable: NavigationPath implementa Codable se tutti i valori nel percorso sono anche Codable + Hashable. Questo consente di salvare e ripristinare lo stato di navigazione quando si riavvia l'app o si passa in background.
.navigationDestination(for:destination:) — un modificatore che registra una vista di destinazione per un tipo di dati specifico. Quando NavigationLink inserisce un valore di questo tipo nel percorso, SwiftUI trova automaticamente il .navigationDestination corrispondente e crea lo schermo.
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"))
Regola importante: .navigationDestination deve essere applicato a una vista che si trova all'interno di NavigationStack e prima che NavigationLink inserisca un valore nel percorso. Di solito viene aggiunto alla vista radice o a un contenitore di sezione. Se non viene trovato alcun .navigationDestination per il tipo del valore, la transizione non avverrà.
Secondo SwiftUI Engineering (2023), .navigationDestination può essere registrato a diversi livelli della gerarchia. SwiftUI cerca la destinazione corrispondente più vicina durante la navigazione. Ciò consente di sovrascrivere la destinazione per un tipo in diverse parti dell'applicazione.
Pattern 1: navigazione tramite enum Route. Definite un enum con valori associati per tutti gli schermi dell'applicazione. Usate un .navigationDestination per AppRoute e uno switch per il routing. Questo fornisce un'unica fonte di verità per tutte le possibili transizioni nell'applicazione.
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)
}
Pattern 2: navigazione programmatica e deep linking. NavigationStack consente la gestione programmatica della pila: aggiunta, rimozione di schermi e ritorno alla radice. Ciò è necessario per notifiche push, deep link e ripristino della navigazione dopo un riavvio.
Pattern 3: array di tipi invece di NavigationPath. Se tutti gli schermi utilizzano lo stesso tipo (ad esempio, String o un enum personalizzato), usate Binding a [T]. Questo offre una tipizzazione più forte e prestazioni migliori rispetto a NavigationPath. NavigationPath è giustificato per pile eterogenee con diversi tipi di schermo.
Secondo Point-Free (2024), NavigationStack con enum Route è il modo preferito per organizzare la navigazione nelle applicazioni SwiftUI. Rende tutte le possibili transizioni esplicite, type-safe e testabili, il che è particolarmente importante per grandi progetti con decine di schermi.
Domande frequenti
NavigationStack — un contenitore di navigazione SwiftUI (iOS 16+) che gestisce una pila di schermi tramite un percorso type-safe. Ha sostituito NavigationView, offrendo supporto per deep linking, navigazione programmatica e preservazione dello stato.
NavigationStack utilizza un percorso type-safe (NavigationPath) invece di legare direttamente NavigationLink a una destinazione. Supporta la navigazione programmatica, il deep linking e Codable per la preservazione dello stato. Funziona su SwiftUI, non tramite UINavigationController.
NavigationPath è un tipo che rappresenta una sequenza di schermi nella pila. Si aggiungono valori al percorso tramite path.append() o NavigationLink con value. Ogni tipo registra un .navigationDestination per la visualizzazione. NavigationPath supporta Codable e la preservazione automatica dello stato.
Attraverso la gestione programmatica del percorso: dopo aver elaborato l'URL, chiamate path.append() con il valore di route corrispondente. NavigationStack visualizza automaticamente lo schermo di destinazione. Ritorno alla radice — path.removeLast(path.count).
Sì, se la vostra versione minima è iOS 16+. NavigationView è deprecato in iOS 17. La migrazione fornisce navigazione type-safe, supporto per deep linking e un migliore allineamento con il ciclo di vita di SwiftUI. Per progetti con iOS 15 e versioni precedenti, continuate a usare NavigationView per ora.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche