SwiftUI предлагает несколько подходов к организации навигации и с выходом
iOS 16 многое изменилось. Разберем ключевые стратегии, которые помогут вам создавать гибкие и поддерживаемые приложения.
Базовая навигация:
Самый простой способ -
NavigationLink:
List(customers) { customer in
NavigationLink {
CustomerDetailScreen(customer: customer)
} label: {
Text(customer.name)
}
}
Минус: инициализация
CustomerDetailScreen происходит для каждого элемента списка.
Альтернатива:
navigationDestination:
List(customers) { customer in
NavigationLink(customer.name, value: customer)
}
.navigationDestination(for: Customer.self) { customer in
CustomerDetailScreen(customer: customer)
}
Плюс: более эффективно, так как обработка навигации централизована.
Динамическая маршрутизация:
Используйте
enum для управления сложными сценариями:
enum Route: Hashable {
case dashboard
case detail(Customer)
}
@State private var routes: [Route] = []
NavigationStack(path: $routes) {
Button("Login") {
routes.append(.dashboard) // Программный переход
}
.navigationDestination(for: Route.self) { route in
switch route {
case .dashboard: DashboardView()
case .detail(let customer): CustomerDetailScreen(customer: customer)
}
}
}
Когда использовать:
- После асинхронных операций (например авторизации).
- Для глубоких ссылок (deeplinks).
Глобальная маршрутизация:
Чтобы избежать передачи
routes через всю иерархию, вынесите логику в
Observable класс:
@Observable
class Router {
var routes: [Route] = []
func navigate(to route: Route) {
routes.append(route)
}
}
// В корневом View:
.environment(Router())
Преимущества:
- Доступ к навигации из любого места.
- Чистый код без проброса состояний.
Навигация через Environment Values:
Вдохновляемся
React и создаем свой
navigate:
struct NavigateAction {
let action: (Route) -> Void
func callAsFunction(_ route: Route) {
action(route)
}
}
// Использование:
@Environment(\.navigate) private var navigate
Button("Войти") {
navigate(.dashboard)
}
Почему это удобно:
- Интуитивный синтаксис.
- Подходит для сложных сценариев.
Навигация во вкладках (TabView):
Каждая вкладка должна иметь свой
NavigationStack:
enum AppScreen: Hashable {
case patients, doctors
}
TabView {
PatientsScreen()
.tabItem {
Label("Пациенты", systemImage: "heart")
}
DoctorsScreen()
.tabItem {
Label("Врачи", systemImage: "stethoscope")
}
}
Важно:
- Не используйте общий Router для вкладок.
- Каждый NavigationStack управляет своим стеком.
Модальные окна и навигация:
Модальные окна - это не часть стека навигации. Для них лучше использовать
@State или отдельный сервис:
.sheet(isPresented: $showModal) {
ModalView()
}
Вывод:
- Для простых приложений - NavigationLink или navigationDestination.
- Для сложных сценариев - глобальный Router + enum маршруты.
- Для вкладок - отдельные NavigationStack на каждую вкладку.
Главное: не усложняйте архитектуру раньше времени. Начинайте с простого и масштабируйте по мере роста приложения.