swiftuiapple-watchwatchoswwdc

Starting View on WatchOS


I'm creating a watchOS app and want to choose the starting view to be the home screen's child. A WWDC20 video (time: 13:50) called it "hierarchical navigation" and showed an example, but did not provide any code.

I've been looking around on how to accomplish this but can not find anything. I'm building the app fully in SwiftUI for watchOS 7+.

How can I start at the child of a view?


Solution

  • Just happened to open SO today and found this question.

    If I understand correctly (based on the wwdc vid you shared), what you want to do is to open a child view in of your main view.

    Let's set up an example based on what I assume you want to do. There is an entry point, which I will name HomeView, hosting your navigation view as well as the navigation links. You will also have two more views (just for illustration purposes), will I'll call AView and BView.

    If I create this new app (StraightToDetailsApp), the main entry point should look like:

    import SwiftUI
    
    @main
    struct StraightToTheDetailsApp: App {
      var body: some Scene {
        WindowGroup {
          Text("Hello world!")
        }
      }
    }
    

    Let's add the three new views we need:

    import SwiftUI
    
    @main
    struct StraightToTheDetailsApp: App {
      var body: some Scene {
        WindowGroup {
          // replaced ContentView with HomeView
          HomeView()
        }
      }
    }
    
    struct HomeView: View {
      var body: some Scene {
        NavigationView {
          ScrollView {
            // specified the navigation hierarchy from home. 
            NavigationLink("Hello A!!!", destination: AView())
            NavigationLink("Greetings B!!!", destination: BView())
          }
        }
      }
    }
    
    // Just made two simple views.
    struct AView: View {
      var body: some Scene {
        VStack {
          Text("Hello from A")
        }.frame(
            minWidth: 0,
            maxWidth: .infinity,
            minHeight: 0,
            maxHeight: .infinity,
            alignment: .center)
         .background(Color.blue)
      }
    }
    
    struct BView: View {
      var body: some Scene {
        VStack {
          Text("Hello from B")
        }.frame(
            minWidth: 0,
            maxWidth: .infinity,
            minHeight: 0,
            maxHeight: .infinity,
            alignment: .center)
         .background(Color.blue)
      }
    }
    

    If you launch your app now, you will see home with two buttons, tapping on it triggers the navigation in the app.

    As you want to force the app to navigate directly to one of the items, you will need to provide identifiers you can use to a bound variable, triggering the navigation automatically. What does that mean?

    // let's create accessors to those links that you can reference from 
    // somewhere in your code.
    // Perhaps, you have a view model maintaining this state.
    enum Details {
      case a
      case b
    }
    
    // And let's adapt home view, with the appropriate state tracking
    // the "tag"
    
    struct HomeView: View {
      @State var selectedTag: Details? = nil
    
      var body: some View {
        NavigationView {
          ScrollView {
            NavigationLink(
              "Hello A!!!",
              destination: AView(),
              tag: Details.a,
              selection: self.$selected)
    
            NavigationLink(
              "Greetings B!!!",
              destination: BView(),
              tag: Details.b,
              selection: self.$selected)
          }
        }
      }
    }
    

    If you launch your app, it will be pretty much as before, but now you can provide a value to that state variable called selectedTag and make it go wherever you want.

    struct HomeView: View {
      @State var selectedTag: Details? = .a
    
      var body: some View {
      // ...
      }
    }
    

    Launching the app now, the app will display AView with HomeView stacked in the navigation.