NavigationLink — это элемент управления в SwiftUI, предназначенный для перехода на другой экран в NavigationStack или NavigationView. По данным Apple Developer Documentation, 2024, NavigationLink создаёт кнопку, при нажатии на которую целевой View помещается в навигационный стек. В iOS 16+ рекомендуется использовать NavigationLink с value и NavigationDestination, а не с destination напрямую, чтобы избежать преждевременной инициализации целевых View.
Главное
NavigationLink — это View, которое при нажатии инициирует навигационный переход. Внутри NavigationStack нажатие на NavigationLink помещает целевой экран на стек и отображает системную кнопку «Назад». NavigationLink существует с iOS 13 и является основным способом пользовательской навигации в SwiftUI.
NavigationLink не наследуется от UIButton — это SwiftUI View, которое автоматически адаптируется к контексту. Внутри List NavigationLink отображается со стрелкой раскрытия (disclosure indicator). Вне списка NavigationLink ведёт себя как обычная кнопка, но с навигационным поведением.
По данным SwiftUI Lab (2024), NavigationLink является одной из самых используемых View в SwiftUI-приложениях, уступая только Text, Image и VStack. Понимание различий между формами инициализации критически важно для производительности и предсказуемого поведения навигации.
При нажатии NavigationLink добавляет значение (или destination) в навигационный стек, связанный с ближайшим NavigationStack или NavigationView. SwiftUI использует EnvironmentValue для передачи пути навигации через иерархию View. NavigationLink читает этот путь из Environment и при нажатии модифицирует его.
NavigationLink имеет две основные формы: с destination (прямое указание целевого View) и с value (значение для NavigationDestination). Выбор формы зависит от версии iOS и архитектуры навигации.
| Форма | Инициализатор | iOS 13–15 | iOS 16+ |
|---|---|---|---|
| Destination | NavigationLink(destination:label:) | Рекомендуется | Не рекомендуется |
| Value | NavigationLink(value:label:) | Недоступна | Рекомендуется |
| IsActive | NavigationLink(isActive:destination:label:) | Программная навигация | Не рекомендуется |
Destination-форма (iOS 13+): NavigationLink(destination: DetailView(), label: { Text("Open") }). Эта форма создаёт DetailView сразу при рендеринге NavigationLink, даже если пользователь не нажал на ссылку. Это приводит к преждевременной инициализации View и потенциальным проблемам с производительностью, если destination View выполняет тяжёлые операции в инициализаторе.
Value-форма (iOS 16+): NavigationLink(value: "detail_42", label: { Text("Open") }). Целевой View создаётся только при нажатии на ссылку, когда SwiftUI находит соответствующий .navigationDestination. Это предотвращает преждевременную инициализацию и делает навигацию более предсказуемой.
NavigationLink с NavigationStack в iOS 16+ требует перехода на value-форму. Вы определяете тип данных для навигации (String, Int, enum Route) и регистрируете destination через .navigationDestination. NavigationLink помещает в стек только значение, а SwiftUI создаёт целевое View при нажатии.
struct CatalogView: View {
let categories: [String]
var body: some View {
List(categories, id: \.self) { category in
NavigationLink(value: category) {
Text(category)
}
}
.navigationDestination(for: String.self) { category in
CategoryView(name: category)
}
}
}
// Programmatic navigation:
struct DeepLinkView: View {
@State private var path: [AppRoute] = []
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .detail(let id): DetailView(id: id)
case .settings: SettingsView()
}
}
.toolbar {
Button("Open Settings") {
path.append(AppRoute.settings)
}
}
}
}
}
Программный переход: добавление значения в path (через path.append) эквивалентно нажатию NavigationLink с таким же значением. Это позволяет реализовать навигацию из ViewModel, Coordinator или в ответ на push-уведомления.
IsActive форма (NavigationLink(isActive:destination:label:)) доступна для совместимости, но не рекомендуется в iOS 16+. Используйте value-форму с Binding к массиву пути или NavigationPath.
NavigationLink в List автоматически отображает стрелку раскрытия (chevron) в правой части строки, сигнализируя пользователю, что нажатие приведёт к переходу на другой экран. List управляет отображением стрелки автоматически — в отличие от обычного NavigationLink вне списка, где стрелки нет.
С iOS 16 List с NavigationLink автоматически использует value-форму внутри List(data:rowContent:). При использовании ForEach внутри List стрелка раскрытия также добавляется автоматически. Это поведение нельзя отключить через модификаторы — только замена NavigationLink на Button может убрать стрелку.
Проблема с destination-формой в List: если вы используете NavigationLink(destination:label:) внутри List, все destination View создаются сразу при загрузке списка, независимо от того, нажал пользователь на ссылку или нет. Для списков с большим количеством строк это может существенно замедлить начальную загрузку и увеличить потребление памяти. Value-форма с NavigationStack решает эту проблему.
По данным WWDC 2022 (Session 10054), Apple рекомендует использовать NavigationStack и value-форму NavigationLink для новых проектов. Это особенно важно для List с динамическими данными, где количество строк может быть большим.
Паттерн 1: кастомный вид NavigationLink. NavigationLink принимает любое View в качестве label, позволяя создавать произвольный дизайн для ссылки. Внутри List это особенно удобно — вы получаете стрелку раскрытия автоматически при использовании NavigationLink.
NavigationLink(value: ProductRoute.detail(product)) {
HStack {
AsyncImage(url: product.imageURL)
.frame(width: 60, height: 60)
VStack(alignment: .leading) {
Text(product.name).font(.headline)
Text(product.price) .foregroundColor(.secondary)
}
}
.padding(8)
}
Паттерн 2: NavigationLink без стрелки (кастомная кнопка). Если вам не нужна стрелка раскрытия, используйте Button для программной навигации: path.append(value). Это полезно для кастомных элементов интерфейса, где NavigationLink выглядит неестественно.
Паттерн 3: условная навигация. Вы можете заблокировать NavigationLink, используя пустое destination или не добавляя .navigationDestination для определённых значений. Программная навигация через path позволяет проверять условия перед добавлением значения.
По данным Hacking with Swift (2024), большинство проблем с NavigationLink связано с использованием destination-формы в старых проектах. При миграции на NavigationStack замените все NavigationLink(destination:label:) на NavigationLink(value:label:) и добавьте .navigationDestination на корневом уровне.
Часто задаваемые вопросы
NavigationLink — это View для перехода на другой экран в SwiftUI. При нажатии помещает целевой экран в навигационный стек NavigationStack или NavigationView. Поддерживает две формы: с destination (целевым View) и с value (значением для маршрутизации).
Value-форма (iOS 16+) предпочтительнее: целевой View создаётся только при нажатии, а не при рендеринге ссылки. Destination-форма создаёт View сразу, что может вызывать проблемы с производительностью. Для проектов с iOS 16+ используйте value + NavigationDestination.
SwiftUI автоматически добавляет disclosure indicator (стрелку) к NavigationLink внутри List, сигнализируя о возможности перехода. Это поведение нельзя отключить. Если стрелка не нужна, используйте Button с программной навигацией через path.append().
Используйте NavigationStack с Binding путём и добавляйте значения через path.append(value). Это эквивалентно нажатию NavigationLink с таким же value. Программная навигация позволяет реализовать Deeplink-и, push-уведомления и Coordinator-паттерн.
Destination-форма может влиять, если целевые View выполняют тяжёлые операции в инициализаторе — все destination создаются при рендеринге списка. Value-форма с NavigationStack решает эту проблему, создавая View только при нажатии. Для списков с 50+ строками разница существенна.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также