티스토리 뷰

반응형

 

안녕하세요 :) 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를 이용하면 딥링크나 상태복원 등등을 쉽게 할 수 있게 됩니다.

 

 

반응형