Search code examples
swiftuiswiftui-navigationlinkswiftui-navigationview

Building navigation between Views in my iOS app


As the time flies, by App get more and more complicated shape.

In some cases the App flow might be:

View A -> View B -> C -> D and then back D -> C -> B -> A..

but sometimes i need to skip the view and go D -> B -> A..

In some cases its A -> C -> D and then D -> A

I started to use NavigationView/NavigationLink and in some cases i use the following approach:

let weekView = WeekView(journey: journey, isRoot: isRoot).environmentObject(self.thisSession)
        window?.rootViewController = UIHostingController(rootView: weekView)

No i realize that it has become a complete mess.. It's time for me to rethink this..

How do you handle navigation in apps where it can't be always done by pushing/popping the views from Navigation stack?


Solution

  • Using ViewBuilders is a good option here.

      @ViewBuilder func myViewRouter(selection: Selection) -> some View {
             switch selection { 
                case selection1:
                    View1()
                case selection2:
                    View2()
                case selection3: 
                    View3()
        
              }
        }
    
     enum Selection { ... }
    

    ViewBuilders are powerful, its pretty much a function that can return opaque types but notice the lack of the return keyword. This seems like a perfect use case for it. In the example I used a enum but its also common to see this used with a var selection = 0 on the parent view and have the ViewBuilder as the child. Either way, same functionality.

    Below is is a good url to understand ViewBuilders.

    https://swiftwithmajid.com/2019/12/18/the-power-of-viewbuilder-in-swiftui/

    Last edit: Here is an example use case:

    import SwiftUI
    
    struct ContentView: View {
        @State private var selection: SelectionEnum = .zero
        var body: some View {
            VStack {
                showMyViews(selection: selection)
                HStack {
                    ForEach(SelectionEnum.allCases, id: \.self) { selection in
                        Button(action: {self.selection = selection}){
                            Text(selection.rawValue)
                                .fontWeight(.bold)
                                .frame(width: 60, height: 60)
                                .foregroundColor(.white)
                                .background(Color.blue)
                                .cornerRadius(10)
                        }
    
                    }
                }
                
                }
    
            
        }
        
        @ViewBuilder func showMyViews(selection: SelectionEnum) -> some View {
            switch selection {
            case .zero:
                ViewA()
            case .one:
                ViewB()
            case .two:
                ViewC()
            case .three:
                ViewD()
            }
        }
        enum SelectionEnum: String, CaseIterable {
            case zero
            case one
            case two
            case three
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    struct ViewA: View {
        var body: some View {
            Text("View A")
        }
    }
    
    struct ViewB: View {
        var body: some View {
            Text("View B")
        }
    }
    
    struct ViewC: View {
        var body: some View {
            Text("View C")
        }
    }
    
    struct ViewD: View {
        var body: some View {
            Text("View D")
        }
    }