이번 글에서는 SwiftUI에서의 Navigation에 대해 학습한 내용을 정리했습니다.

Navigation | Apple Developer Documentation

1️⃣ NavigationStack에서 목적지를 제시하는 방법

View-destination

NavigationLink(destination:label:)

label: 내비게이션 링크에 표시되는 ViewBuilder

destination: 사용자가 링크를 탭 했을 때 표시되는 ViewBuilder

예시

**struct** ViewDestinationExample: View {
    
    **var** body: **some** View {
        NavigationStack {
            VStack(spacing: 15) {
                Group {
                    NavigationLink {
                        IconView(icon: OperatorSymbol.plus.rawValue) // 목적지 뷰
                    } label: {
                        Text("PLUS") // 링크에 표시될 뷰
                    }
                    
                   // ... 다른 링크들
                }
                .padding(8)
                .background(Color(.black))
                .foregroundStyle(.gray)
                .clipShape(RoundedRectangle(cornerRadius: 12))
                .navigationTitle("Example 01")
            }
        }
    }
    
}

ex01.gif

<aside>

👀 init(destination:label:)을 사용할 때 참고할 점

SwiftUI가 내비게이션 상태와 내비게이션 경로의 content를 추적합니다.

  1. 언제 뷰가 푸시되었는지 감지할 방법이 없습니다. (viewDidAppear 처럼 해당 시점을 감지할 수 있는 방법이 없다는 의미!)
  2. 내비게이션의 상태를 프로그래밍적으로 복원할 수 없습니다.

다른 방법을 통해 트리거 시점과 프로그래밍적인 복원이 가능합니다! 아래 내용에 그 방법이 포함되어 있습니다.

</aside>

Value-destination

navigationDestination(for:destination:)

NavigationLink(value:label:)
navigationDestination(for:destination:) // view modifier

내비게이션 경로에 데이터를 넣는다면, SwiftUI는 그 데이터 타입를 뷰에 매핑합니다.

그리고 사용자가 링크를 탭하면 해당 데이터를 내비게이션 스택에 올립니다.

해당 스택이 보여줄 뷰를 정하기 위해 위 view modifier를 해당 NavigationStack 내부에서 사용합니다.

Value-based 내비게이션은 목적지의 타입이 두 개 이상인 경우 유용합니다.

SwiftUI는 NavigationLinkvalue의 타입을 바탕으로 해당하는 목적지 뷰를 결정합니다.

예시

struct ValueDestinationExampleFor: View {
    
    var body: some View {
        NavigationStack {
            List {
                Section(header: Text("Weather")) {
                    ForEach(WeatherType.allCases) { weather in
                        NavigationLink(weather.description, value: weather)
                    }
                }
                
                Section(header: Text("Time Slot")) {
                    ForEach(TimeSlotType.allCases) { slot in
                        NavigationLink(slot.description, value: slot)
                    }
                }
            }
            .navigationDestination(for: WeatherType.self) { weather in
                IconView(icon: weather.rawValue)
            }
            .navigationDestination(for: TimeSlotType.self) { slot in
                IconView(icon: slot.rawValue)
            }
            .navigationTitle("Example 02")
        }
    }
    
}

ex02.gif