Skip to content

Instantly share code, notes, and snippets.

@woozoobro
Created July 18, 2023 15:47
Show Gist options
  • Select an option

  • Save woozoobro/55f5a03c0c9b3104d6fba9ecab42c933 to your computer and use it in GitHub Desktop.

Select an option

Save woozoobro/55f5a03c0c9b3104d6fba9ecab42c933 to your computer and use it in GitHub Desktop.
NavigationStack 사용해보기

https://developer.apple.com/documentation/swiftui/navigationview

NavigationView가 deprecated 된다고 합니다 NavigationStack의 Minimum은 iOS 16+

기존 NavigationView 테스트

struct TestNavigationStack: View {
    var body: some View {
        NavigationView {
            List(0...5, id: \.self) { index in
                NavigationLink {
                    FirstDestinationView(viewModel: FirstDestinationViewModel())
                } label: {
                    Text("\(index)번째 버튼")
                        .font(.largeTitle)
                        .bold()
                }
            }.navigationTitle("네비게이션 스택 연습")
        }
    }
}

@MainActor
class FirstDestinationViewModel: ObservableObject {
    @Published var image: UIImage? = nil
    init() {
        downloadImage()
    }
    
    func downloadImage() {
        Task {
            let url = URL(string: "https://picsum.photos/200")!
            let (data, _) = try await URLSession.shared.data(from: url)
            if let image = UIImage(data: data) {
                self.image = image
                print("이미지가 다운로드 되었습니다")
            }
        }
    }
    
}

struct FirstDestinationView: View {
    @StateObject private var viewModel: FirstDestinationViewModel
    
    init(viewModel: FirstDestinationViewModel) {
        print("목적지가 되는 뷰가 생성 되었습니다!")
        _viewModel = StateObject(wrappedValue: viewModel)
    }
    
    var body: some View {
        VStack {
            if let image = viewModel.image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 200, height: 200)
            }
            Text("목적지가 되는 뷰")
        }
    }    
}

프린트문을 찍어보면~!

목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다

오잇...!

다 다운되고 있네요

기존 NavigationView는 Destination이 되는 View도 진입하기 전에 미리 init �해버린다!


그럼 NavigationStack을 써볼까요!

struct TestNavigationStack: View {
    var body: some View {
        NavigationStack {
            List(0...5, id: \.self) { index in
                NavigationLink(value: index) {
                    Text("\(index) 버튼")
                        .font(.largeTitle)
                        .bold()
                }
            }
            .navigationTitle("네비게이션 스택 연습")
            .navigationDestination(for: Int.self) { value in
                FirstDestinationView(viewModel: FirstDestinationViewModel())
            }
        }
    }
}
목적지가 되는 뷰가 생성 되었습니다!
목적지가 되는 뷰가 생성 되었습니다!
이미지가 다운로드 되었습니다
이미지가 다운로드 되었습니다

오케이! 진입을 해야 init이 되네요 이제!

(그럼에도 두번이 찍히는 건..) https://developer.apple.com/forums/thread/718281


NavigationStack(path: root:)

이번엔 코드로 Navigation을 이동시키는 걸 해보겠습니다.

struct TestNavigationStack: View {
    let appleProducts = ["Mac", "Macbook", "iPhone", "iPad"]
    
    @State private var stackPath: [String] = []
    
    var body: some View {
        NavigationStack(path: $stackPath) {
            List(appleProducts, id: \.self) { product in
                NavigationLink(value: product) {
                    Text(product)
                }
            }
            .navigationDestination(for: String.self) { product in
                Text("\(product) has Clicked!")
            }
            
            Button {
                stackPath.append("Mac")
            } label: {
                Text("네비게이션 프슝")
                    .font(.largeTitle)
                    .bold()
            }
        }
    }
}

타입에 상관없이 path를 관리하고 싶다면!

NavigationPath 타입을 사용해주면 됩니다!

struct TestNavigationStack: View {
    let appleProducts = ["Mac", "Macbook", "iPhone", "iPad"]
    
    @State private var stackPath: NavigationPath = .init()
    
    var body: some View {
        NavigationStack(path: $stackPath) {
            List(appleProducts, id: \.self) { product in
                Button {
                    stackPath.append(product)
                } label: {
                    Text(product)
                        .font(.largeTitle)
                        .bold()
                }
                
                Button {
                    stackPath.append(1)
                } label: {
                    Text("For Int")
                }

            }
            .navigationDestination(for: String.self) { product in
                Text("\(product) has Clicked!")
            }
            .navigationDestination(for: Int.self) { int in
                Text("\(int)")
            }
        }
    }
}

좀 더 깊숙히 들어가볼까요!

struct TestNavigationStack: View {
    let appleProducts = ["Mac", "Macbook", "iPhone", "iPad"]
    
    @State private var stackPath: NavigationPath = .init()
    
    var body: some View {
        NavigationStack(path: $stackPath) {
            List(appleProducts, id: \.self) { product in
                Button {
                    stackPath.append(product)
                } label: {
                    Text(product)
                        .font(.largeTitle)
                        .bold()
                }
                
                Button {
                    stackPath.append(1)
                } label: {
                    Text("For Int")
                }
                
            }
            .navigationDestination(for: String.self) { product in
                AppleProductView(product: product, path: $stackPath)
            }
            .navigationDestination(for: Int.self) { int in
                Text("\(int)")
            }
        }
    }
}

struct AppleProductView: View {
    let product: String
    @Binding var path: NavigationPath
    
    var body: some View {
        VStack(spacing: 20) {
            Text(product)
                .font(.largeTitle)
                .onTapGesture {
                    path.append("세번째 데스티네이션..!!")
                }
            
            Text("모두 없애버리기")
                .onTapGesture {
                    path = .init()
                }
        }
    }
}

NavigationStack에 관련된 기본은 이 정도가 될 것 같아요

다음 영상들에서 해볼 건 만약에 같은 타입에 같은 모델일 때 도착지점이 다르게 하고 싶다면 어떻게 코드를 구성해야할지에 대해서! NavigationPath를 지금처럼 View가 들고 있지 않고 다른 객체가 들고 있게 하는 것도 해보고, 또 NavigationStack과 TabView를 같이 사용할 때는 어떻게 해야할지 한번 고민해보면 좋을 것 같아요

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment