[SwiftUI] NavigationView ➡️ NavigationStack
안녕하세요 :) Zedd입니다.
@available(iOS, introduced: 13.0, deprecated: 100000.0, message: "use NavigationStack or NavigationSplitView instead")
public struct NavigationView<Content> : View where Content : View { ... }
WWDC22에서 NavigationView가 deprecated되고 NavigationStack/NavigationSplitView가 나왔는데...한번 바꿔보려고 합니다.
💡 NavigationStack/NavigationSplitView가 iOS 16+ 부터 사용할 수 있기 때문에, 앱의 Deployment Target이 16+ 이상인경우에만 진행하는게 좋습니다.
이 글에서 NavigationSplitView는 따로 다루지 않습니다.
# NavigationStack
RootView를 표시하고, Root View에 대한 추가(additional) View를 제공할 있는 View
앱이 굳이 Split 되어서 보여질 필요가 없다면, 즉 Single column navigation을 사용한다면 NavigationStack을 사용하는 것이 좋습니다.
기존 (deprecated된) NavigationView를 사용할때는
NavigationView {
/* content */
}
.navigationViewStyle(.stack) ✅
이런식으로 navigationViewStyle을 stack으로 주었을텐데,
NavigationStack을 사용한다면
NavigationStack {
/* content */
}
이렇게만 사용하면 됩니다.
Stack은 항상 제거되지 않은 가장 최근에 추가된 View를 표시하며, RootView는 제거할 수 없다!
# NavigationLink와 .navigationDestination
NavigationStack을 사용한다면, NavigationLink와 .navigationDestination modifier는 그냥 세트로 다닌다고 생각하면 됩니다.
✔️ NavigationLink - 탐색 Presentation을 제어하는 View.
✔️ navigationDestination(for:destination:) - Destination View를 NaviationLink에서 제시된 Data 타입과 연결해주는 Method
var body: some View {
NavigationStack {
List {
NavigationLink("Mint", value: Color.mint)
NavigationLink("Pink", value: Color.pink)
NavigationLink("Teal", value: Color.teal)
}
.navigationTitle("Colors")
}
}
이렇게만 해주면
이렇게 Navigation을 위한 View들만 만들어지고, 클릭하면 아무런 이동도 안하는데요. (NaviationLink가 View니까)
우리가 원하는 것 -> 눌렀을 때 내가 원하는 1) View로 2) Navigation되었으면 좋겠어.
[준비물]
1. 눌렀을 때 이동할 View
struct ColorDetail: View {
var color: Color
var body: some View {
Text("\(self.color.description)")
}
}
2. navigationDestination(for:destination:) 사용
var body: some View {
NavigationStack {
List {
NavigationLink("Mint", value: Color.mint)
NavigationLink("Pink", value: Color.pink)
NavigationLink("Teal", value: Color.teal)
}
.navigationDestination(for: Color.self) { color in ✅
ColorDetail(color: color)
}
.navigationTitle("Colors")
}
}
⚠️ 주의할 점 ⚠️
NavigationLink("Teal", value: Color.teal)
NavigationLink에 제시된 Data타입 ➡️ Color
.navigationDestination(for: Color.self)
navigationDestination(for:destination:) ➡️ Color
이 Data 타입이 같지 않으면 Navigation을 하지 않으니 주의!
Q : 나는 List에 여러 Data타입이 들어있는데... 🥹
NavigationLink("Mint", value: Color.mint) ➡️ Color 타입
NavigationLink("Black", value: "Black") ➡️ String 타입
A : navigationDestination(for:destination:)를 여러개 쓰면 됨...
List {
NavigationLink("Mint", value: Color.mint)
NavigationLink("Black", value: "Black")
}
.navigationDestination(for: Color.self) { color in
ColorDetailForColor(color: color) // Color 타입 용
}
.navigationDestination(for: String.self) { color in
ColorDetailForString(color: color) // String 타입 용
}
이런식으로!!
기존 NavigationView를 사용했다면
var body: some View {
NavigationView { // This is deprecated.
List {
NavigationLink("Purple") {
ColorDetail(color: .purple)
}
NavigationLink("Pink") {
ColorDetail(color: .pink)
}
NavigationLink("Orange") {
ColorDetail(color: .orange)
}
}
}
.navigationViewStyle(.stack)
}
이런식으로 NavigationLink마다 Destination View를 지정했어야 했는데...훨씬 간결해지고 편해진 것 같아요?
물론 위 코드도 NavigationLink를 1개만 쓰도록 개선될 수 있겠지만.. 일단 그렇다~
# Manage navigation state
기본적으로 NavigationStack은 state를 관리하여 stack에 있는 view들을 추적하는데,
이걸 내가 직접 관리할수도 있습니다.
@State private var presentedColor: [Color] = [] ✅
var body: some View {
NavigationStack(path: $presentedColor) { ... } ✅
}
이런식으로 State변수를 만들고, NavigationStack의 생성자 파라미터인 path에 넣어줍니다.
이제 NavigationStack은 우리가 만든 presentedColor State를 관찰하게 됩니다.
이제 presentedColor에 값들을 넣어봅시다.
@State private var presentedColor: [Color] = [Color.brown, Color.cyan]
이제 앱을 실행하면
앱을 실행하자마자
@State private var presentedColor: [Color] = [Color.brown, Color.cyan]
root > brown > cyan 순으로 stack이 쌓인것을 확인 할 수 있습니다.
NavigationLink 를 사용하지 않고
List {
NavigationLink("Mint", value: Color.mint)
NavigationLink("Pink", value: Color.pink)
NavigationLink("Teal", value: Color.teal)
Button("DeepLink", action: {
self.showColors()
})
}
func showColors() {
self.presentedColor = [Color.gray, Color.green]
}
이런식으로 제가 직접 값을 지정해주거나, 제거해주면 그대로 Navigation Stack이 수정됩니다.
Button을 클릭했을 때 presentedColor 값을 수정해주는 메소드를 호출하게 했습니다.
보신 것 같이 NavigationStack의 path를 이용하면 딥링크나 상태복원 등등을 쉽게 할 수 있게 됩니다.