Search code examples
swiftuiswiftui-navigationstackswiftui-navigationswiftui-navigationpath

A complex Navigation with NavigationPath in SwiftUI


The usual flow is the stack. Push views and pop views. My requirements is a bit complex and I did not find a way to do it in IOS 16.

Let say we have a

  • Cover page
  • Contents page
  • Details page

Now, the requirements;

  1. Cover page can push the Details page ; this is easy.
  2. Cover page can push the Contents page ; this is also easy.
  3. Contents page can push the Details page ; this is also easy.

Now;

  1. Whatever way the Details page is pushed (1 or 3), it must return to Contents page

Is this a possible requirement? If so, any guidance is welcome.

PS: sorry no code since there is no solution that I've figured!


Solution

  • NavigationPath is a stack that doesn't allow insertion. Instead one can use array as used in hackingwithswift. Each page is represented with a number except the root page.

    There is a nice feature of the path; one doesn't need to dismiss the page with environmental presentation mode (see). Once the last number is removed the page dismissed.

    The insertion is easy, however, the animation doesn't exist when returning to the inserted page. This is a bug, I think.

    Yes, the new navigation opens the path to complex navigations.

    import SwiftUI
    
    struct NavPathTestView: View {
        
        @State var presentedNumbers = [Int]()
    
            var body: some View {
                NavigationStack(path: $presentedNumbers) {
                    NavigationLink(value: 1) {
                        HStack {
                            Text("Contents")
                        }.padding()
                            .foregroundColor(.white)
                            .background(Color.red)
                            .cornerRadius(40)
                    }
                    NavigationLink(value: 2) {
                        HStack {
                            Text("Reader Page")
                        }.padding()
                            .foregroundColor(.white)
                            .background(Color.red)
                            .cornerRadius(40)
                    }
    
                    .navigationDestination(for: Int.self) { i in
                        if i == 1 {
                            ContentsPageView(presentedNumbers: $presentedNumbers)
                        } else {
                            ReaderPageView(presentedNumbers: $presentedNumbers)
                        } 
                    }
                    .navigationTitle("Cover Page")
                }
            }
    }
    
    struct ReaderPageView: View {
        
        @Binding var presentedNumbers : [Int]
        
        var body: some View {
            
            
            VStack {
                Text("Reader Page")
                Spacer()
                Button (action: {
                    
                    if (presentedNumbers.count == 2) && ( presentedNumbers.last == 2 ) {
                        presentedNumbers.removeLast()
                    } else {
                        withAnimation() {
                            presentedNumbers.insert(1, at: 0)
                            presentedNumbers.removeLast()
                        }
                    }
                })
                {
                    HStack {
                        Text("Back To Contents")
                    }.padding()
                        .foregroundColor(.white)
                        .background(Color.red)
                        .cornerRadius(40)
                }
            }
        }
    
    
    }
    
    
    struct ContentsPageView: View {
          
        @Binding var presentedNumbers : [Int]
        
        var body: some View {
            
            VStack() {
                
                Text("Contents Page")
                Spacer()
                Button (action: {
                    
                    presentedNumbers.append(2)
                })
                {
                    HStack {
                        Text("Go to Read")
                    }.padding()
                        .foregroundColor(.white)
                        .background(Color.red)
                        .cornerRadius(40)
                }
            }
            
        }
    }
    
    
    struct NavPathTestView_Previews: PreviewProvider {
        static var previews: some View {
            NavPathTestView()
        }
    }
    

    Many thanks to lorem-ipsum for their constructive comments.


    Update: if the page loading time is fast then it can turn back the inserted page with animation, however, on a page that takes time, the animation is not performed. This is my observation and maybe the reason is something else.