Зададим собственное навигационное представление с помощью двух generic-типов. Первый тип — Content, который применяется для передачи представлений внутри кастомного навигационного представления. Второй тип — Destination, который используется для передачи любого целевого представления.
struct CustomNavigationView<Content: View, Destination : View>: View {
}
Content— универсальный тип, соответствующий представлениюView. Он позволяет добавить закрытие, что дает несколько дочерних представлений.Destination— универсальный тип, соответствующий представлениюView. Он применяется для передачи целевой ссылки.
Константы и переменные
Добавьте в навигационное представление переменные и константы, приведенные ниже. Воспользуйтесь init(), чтобы дополнить содержимое атрибутом @ViewBuilder.
struct CustomNavigationView<Content: View, Destination : View>: View {
let destination : Destination
let isRoot : Bool
let isLast : Bool
let color : Color
let content: Content
@State var active = false
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
init(destination: Destination, isRoot : Bool, isLast : Bool,color : Color, @ViewBuilder content: () -> Content) {
self.destination = destination
self.isRoot = isRoot
self.isLast = isLast
self.color = color
self.content = content()
}
var body: some View {
// некоторые представления
}
}
destinationнеобходимо для передачи целевого представления в кастомное навигационное представление. Если целевое представление отсутствует, задайте емуEmptyView().isRoot— логическое значение для создания корневого представления. Оно понадобится для управления прозрачностью кнопки со стрелкой назад.isLast— логическое значение для определения последнего представления. В данном руководстве это значение понадобится для управления прозрачностью кнопки с навигационной ссылкой.colorнеобходим для настройки цвета панели навигации.contentобеспечивает закрытие для передачи дочерних представлений в кастомное навигационное представление.active— переменная состояния для управления навигационной ссылкой.mode— обработчик переменных окружения для отклонения представлений.
Облик навигационной панели
Для панели навигации вы можете воспользоваться HStack с указанием цвета фона или создать пользовательскую форму и разместить поверх элементы панели навигации. В этом руководстве мы создадим форму с очертаниями волны и поместим ее в ZStack, а затем добавим HStack с навигационными элементами.
struct WaveShape : Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: .zero)
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addCurve(to: CGPoint(x: rect.minX, y: rect.maxY),
control1: CGPoint(x: rect.maxX * 0.75, y: rect.maxY * 0.5),
control2: CGPoint(x: rect.maxX * 0.35, y: rect.maxY * 2))
return path
}
}
Тело кастомного представления навигации
В переменную body добавьте GeometryReader внутри NavigationView. GeometryReader понадобится, чтобы поправить размеры элементов пользовательского интерфейса. В GeometryReader добавьте параметр color, который расширит размер представления до размера экрана. Под color добавьте VStack, который будет содержать WaveShape(), созданный нами выше. Вставьте его в ZStack и отрегулируйте рамку. Чтобы удалить безопасную область в верхней части экрана, воспользуйтесь модификатором .edgesIgnoringSafeArea(.top).
В том же VStack разместите константу content с теми модификаторами по вашему выбору. Между всеми элементами в VStack вставьте Spacer(). Это должно переместить кастомную навигационную панель в верхнюю часть экрана, а содержимое — в центр экрана.
NavigationView нужно только для того, чтобы заработала навигационная ссылка. Нам не понадобится панель навигации по умолчанию, которая поставляется вместе с NavigationView. Чтобы удалить ее, добавьте модификатор .navigationBarHidden(true).
var body: some View {
NavigationView {
GeometryReader { geometry in
Color.white
VStack {
ZStack {
WaveShape()
.fill(color.opacity(0.3))
// элементы панели навигации должны располагаться здесь, внутри HStack
}
.frame(width: geometry.size.width, height: 90)
.edgesIgnoringSafeArea(.top)
Spacer()
self.content
.padding()
.background(color.opacity(0.3))
.cornerRadius(20)
Spacer()
}
}.navigationBarHidden(true)
}
}
}
Панель навигации
Последнее, что нам нужно, — добавить элементы в навигационную панель. Для этого руководства мы воспользуемся центрированным изображением, которое можно применить в качестве логотипа, и двумя изображениями со стрелками “влево” и “вправо” с жестами касания для навигации между представлениями. Убедитесь, что изображения одной ширины, и добавьте между ними Spacer().
Внутри жеста касания первой стрелки разместите переменную mode, чтобы закрыть текущее представление. Внутри жеста касания второй стрелки подключите переменную active, которую мы определили в начале этого руководства.
Добавьте NavigationLink и передайте его переменным active и destination. Задайте представлению destination модификатор .navigationBarHidden(true).
HStack {
Image(systemName: "arrow.left")
.frame(width: 30)
.onTapGesture(count: 1, perform: {
self.mode.wrappedValue.dismiss()
}).opacity(isRoot ? 0 : 1)
Spacer()
Image(systemName: "command")
.frame(width: 30)
Spacer()
Image(systemName: "arrow.right")
.frame(width: 30)
.onTapGesture(count: 1, perform: {
self.active.toggle()
})
.opacity(isLast ? 0 : 1)
NavigationLink(
destination: destination.navigationBarHidden(true)
.navigationBarHidden(true),
isActive: self.$active,
label: {
//никакого ярлыка
})
}
.padding([.leading,.trailing], 8)
.frame(width: geometry.size.width)
.font(.system(size: 22))
Содержимое
Создайте несколько представлений для навигации между ними. Внутри каждого создаваемого представления задействуйте CustomNavigationView. В закрытии добавьте содержимое, которое должно отображаться в пользовательском интерфейсе.
Взгляните на пример ниже, чтобы понять, как заполнить параметры.
struct ContentView: View {
var body: some View {
CustomNavigationView(destination: FirstView(), isRoot: true, isLast: false, color: .blue){
Text("This is the Root View")
}
}
}
struct FirstView : View {
var body: some View {
CustomNavigationView(destination: SecondView(), isRoot: false, isLast: false, color: .red){
Text("This is the First View")
}
}
}
struct SecondView : View {
var body: some View {
CustomNavigationView(destination: LastView(), isRoot: false, isLast: false, color: .green){
Text("This is the Second View")
}
}
}
struct LastView : View {
var body: some View {
CustomNavigationView(destination: EmptyView(), isRoot: false, isLast: true, color: .yellow){
Text("This is the Last View")
}
}
}
Чтобы ознакомиться с кодом целиком, нажмите сюда.
Читайте также:
- Как работает проверка доступности API в Swift
- Полезные глобальные функции языка Swift
- Понимание врапперов в Swift
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Sarah: Create a Custom Navigation View in SwiftUI





