Ответ
Да, это возможно и часто рекомендуется для уменьшения ненужных перерисовок View. Вот основные подходы:
1. Использование @EnvironmentObject Инжектируйте общий роутер во все дочерние View через окружение.
class Router: ObservableObject {
@Published var path = NavigationPath()
}
// В корне приложения
ContentView()
.environmentObject(Router())
// В любой дочерней View
struct DetailView: View {
@EnvironmentObject var router: Router
var body: some View {
Button("Back") {
router.path.removeLast()
}
}
}
2. Передача Binding Для простых модальных окон или переключений.
struct ParentView: View {
@State private var showSheet = false
var body: some View {
ChildView(isPresented: $showSheet)
}
}
struct ChildView: View {
@Binding var isPresented: Bool
var body: some View {
Button("Close") { isPresented = false }
}
}
3. Координатор или Router-паттерн Навигация выносится в отдельный класс, который инжектируется через инициализатор.
protocol AppRouter {
func navigate(to destination: Destination)
}
struct ContentView: View {
let router: AppRouter
var body: some View {
Button("Go to Details") {
router.navigate(to: .detail(id: 1))
}
}
}
Преимущества:
- Меньше перерисовок — View не подписывается на изменения через
@ObservedObject. - Лучшая инкапсуляция — состояние навигации управляется централизованно.
- Проще тестирование — роутер можно легко подменить.