iyOmSd/Title: SwiftUI

[SwiftUI] NavigationStack

냄수 2023. 5. 29. 15:29
반응형

안녕하세요!

SwiftUI에서 항상 느꼇던 불편한점중 하나가 네비게이션이였는데요

이를 해결해주는게 나온지 좀 됐지만 이제 해보려합니다!

(진작에 나왔을 녀석이여야 했는데...)

iOS16 타겟을 쓸일이 없지만... 곧 쓸 지도 모르니까요! 😁

타입은 새롭지만 저희한텐 익숙합니다

push pop되는 그런 인터페이스를 나타내기때문이죠!

 

이것말고도 NavigationSplitView 타입도 있어요 mac이나 iPad에서 사용하는 다중열을 보여줄 때 사용하는 타입이죠

NavigationSplitView은 스킵하고 오늘은 NavigationStack을 알아보려합니다

 

이전에는 NavigationLink를 이용한 방식으로

var body: some View {
    NavigationView {
        NavigationLink {
            뷰(color: .red, order: 1)
        } label: {
            Text("타이틀")
        }
    }
}

이렇게 타이틀과 뷰를 넣어주는 식으로 사용됬다면

 

새로운 버전으로는 뷰 대신에 값을 전달합니다.

var body: some View {
    NavigationStack {
        NavigationLink(value: Color.brown) {
            뷰(color: .orange, order: 2)
        }
        .navigationDestination(for: Color.self) { color in
            뷰(color: color, order: 3)
        }
    }
}

NavigationLink에 hashable한 value를 넣어주면

.navigationDestination 메서드로 값을 받아와서 값에따라 원하는 뷰를 그려줄 수 있어요

 

NavigationStack은 Stack이 표시하는 모든데이터를 나타내는 경로를 추적할 수 있어요

위의 예제는 생성자에서 path를 생략했지만 필요에 따라서 path를 바인딩 해줄 수 있어요

 

루트만 표시하는경우 path는 비어있고

스택내부 또는 스택에 푸시된 뷰 내부에서 선언된 모든 네비게이션 대상을 추적합니다

경로가 비었기때문에 위의 그림처럼 푸시된 뷰 목록도 비어있는 상태입니다.

 

Apple Pie 목록을 클릭해서 푸시된다면

path에 추가되고

스택은 경로값에 대상을 매핑하여 스택에서 푸시할 뷰를 결정합니다

 

뒤로가기 버튼을 누르면 스택이 경로 및 푸시된 뷰에서 마지막 항목을 제거합니다.

 

path를 사용한 네비게이션은 간단하게 배열에 값을 추가하고 제거하는 과정을 통해 라우팅을 컨트롤 할 수 있어요

struct TestNavigationStack: View {
    @State private var path: [Color] = []
    
    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                Button {
                    path.append(.yellow)
                } label: {
                    Text("나랑노랑")
                }
            }
            .navigationDestination(for: Color.self) { color in
                뷰(color: color, order: 3)
                    .overlay {
                        VStack {
                            Button {
                                path.append([Color.red,Color.orange,Color.blue,Color.gray,Color.indigo].randomElement()!)
                            } label: {
                                Text("랜덤푸시")
                            }
                            Button {
                                path.removeAll()
                            } label: {
                                Text("초기화")
                            }
                        }
                    }
            }
        }
    }
}

뒤로가기, root로 이동

 

이를 이용해서 이젠 SwiftUI에서도 코디네이터 방식도 손쉽게 해볼 수 있을 것 같다고 생각들었어요

간단하게 프로토타입만 작성해봤는데 동작은 잘 하더라구요

@main
struct test_swiftUIApp: App {
    @StateObject var coordinator: Coordinator = Coordinator()
    
    var body: some Scene {
        WindowGroup {
            NavigationStack(path: $coordinator.route) {
                TestNavigationStack()
                    .environmentObject(coordinator)
                    .navigationDestination(for: Color.self) { color in
                        뷰(color: color, order: 3)
                            .overlay {
                                VStack {
                                    Button {
                                        coordinator.push([Color.red,Color.orange,Color.blue,Color.gray,Color.indigo].randomElement()!)
                                    } label: {
                                        Text("랜덤푸시")
                                    }
                                    Button {
                                        coordinator.popToRoot()
                                    } label: {
                                        Text("초기화")
                                    }
                                }
                            }
                    }
            }
        }
    }
}
struct TestNavigationStack: View {
    @EnvironmentObject var coordinator: Coordinator
    
    var body: some View {
        VStack {
            Button {
                coordinator.push(.yellow)
            } label: {
                Text("나랑노랑")
            }
        }
    }
}
final class Coordinator: ObservableObject {
    @Published var route: [Color] = []
    
    func push(_ color: Color) {
        route.append(color)
    }
    
    func pop() {
        route.removeLast()
    }
    
    func popToRoot() {
        route.removeAll()
    }
}

 

NavigationStack을 사용할 때 주의할 점으로

NavigationStack 안쪽에 navigationDestination가 존재해야해요

인식하지 못하기 때문에 바깥에 쓴것과 navigationDestination를 정의하지 않는 것과 동일하죠

두 경우 모두 경고표시의 뷰를 볼 수 있습니다.

 

 

 

 

참조

https://developer.apple.com/wwdc22/10054

반응형