Ответ
Технически — да, но это не рекомендуется для статической навигации, так как ухудшает читаемость и поддержку кода. Лучше использовать отдельные NavigationLink для каждого экрана.
Один NavigationLink с параметрами tag и selection оправдан только для динамической навигации, где пункты назначения определяются во время выполнения:
enum AppRoute { case profile, settings, detail(id: UUID) }
struct ContentView: View {
@State private var activeRoute: AppRoute?
var body: some View {
NavigationView {
VStack {
Button("Open Profile") { activeRoute = .profile }
Button("Open Settings") { activeRoute = .settings }
}
.background(
NavigationLink(
destination: destinationView(for: activeRoute),
tag: activeRoute,
selection: $activeRoute,
label: { EmptyView() }
)
)
}
}
@ViewBuilder
private func destinationView(for route: AppRoute?) -> some View {
switch route {
case .profile: ProfileView()
case .settings: SettingsView()
case .detail(let id): DetailView(id: id)
case nil: EmptyView()
}
}
}
Современный подход (iOS 16+): Используйте NavigationStack с модификатором navigationDestination, который декларативно связывает значение с представлением:
NavigationStack(path: $navigationPath) {
RootView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile: ProfileView()
case .settings: SettingsView()
}
}
} Ответ 18+ 🔞
Смотри, тут такая история, как с этим твоим NavigationLink. Технически-то, блядь, можно, конечно. Но это как ебать кота за хвост — вроде и получается, но всем хуёво и непонятно зачем.
Вот представь: ты пишешь статическую навигацию, где всё известно заранее. И вместо того, чтобы просто на каждый пункт меню нацепить свой NavigationLink, ты начинаешь этот цирк с tag и selection. Зачем, сука? Код превращается в такую кашу, что через неделю сам себя ебнешь, пытаясь вспомнить, какой case за какой экран отвечает. Читаемость — в пизду, поддержка — ноль ебать. Просто не надо так.
Этот фокус с одним линком оправдан только в одном случае — когда у тебя динамическая навигация. То есть ты заранее не знаешь, куда тебя пошлют. Например, по глубокой ссылке или после какого-то асинхронного хуяк-хуяк.
enum AppRoute { case profile, settings, detail(id: UUID) }
struct ContentView: View {
@State private var activeRoute: AppRoute?
var body: some View {
NavigationView {
VStack {
Button("Open Profile") { activeRoute = .profile }
Button("Open Settings") { activeRoute = .settings }
}
.background(
NavigationLink(
destination: destinationView(for: activeRoute),
tag: activeRoute,
selection: $activeRoute,
label: { EmptyView() }
)
)
}
}
@ViewBuilder
private func destinationView(for route: AppRoute?) -> some View {
switch route {
case .profile: ProfileView()
case .settings: SettingsView()
case .detail(let id): DetailView(id: id)
case nil: EmptyView()
}
}
}
Видишь? Тут логика есть. Мы не знаем, какой route прилетит, поэтому один универсальный линк — самое то.
Но если ты на iOS 16+, то вообще забудь эту старую хуйню. Там есть NavigationStack, и это просто ёперный театр, как удобно!
NavigationStack(path: $navigationPath) {
RootView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile: ProfileView()
case .settings: SettingsView()
}
}
}
Вот это, блядь, красота! Всё декларативно, всё на своих местах. Никаких костылей в виде EmptyView() и скрытых линков. Apple, сука, наконец-то додумалась.
Короче, резюме: не умничай просто так. Для статики — отдельные линки, для динамики — можно один с selection, а для нового стека — ваще песня. Выбирай по обстановке, а не потому что "а я так умею".