6

I need to pop to the root view from a deep detail view. And while the following solution using isDetailLink and isActive works quite well for iOS, it does not work for watchOS. The isDetailLink command is unavailable in watchOS.

'isDetailLink' is unavailable in watchOS

import SwiftUI

class AppState : ObservableObject {
    @Published var showState : Bool = false
}

struct MoreTests: View {
    @EnvironmentObject var appState : AppState    // injected from SceneDelegate
    var body: some View {
        NavigationView {
            NavigationLink(
                destination: MoreView1(),
                isActive: $appState.showState,      // required to work
                label: { Text("Go to MoreView1") }
            ).isDetailLink(false)       // required to work
        }.navigationBarTitle("Root")
    }
}

struct MoreView1: View {
    var body: some View {
        NavigationLink(
            destination: MoreView2(),
            label: { Text("Go to MoreView2") }
        )
        .navigationBarTitle("MoreView1")
    }
}

struct MoreView2: View {
    var body: some View {
        NavigationLink(
            destination: MoreView3(),
            label: { Text("Go to MoreView3") }
        )
        .navigationBarTitle("MoreView2")
    }
}

struct MoreView3: View {
    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var appState : AppState
    var body: some View {
        VStack {
            Button(action: {
                self.appState.showState = false       // required
            }) {
                Text("Dismiss to root")
            }
        }.navigationBarTitle("MoreView3")
    }
}

The iOS solution came from How can I pop to the Root view using SwiftUI?.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sid
  • 963
  • 1
  • 12
  • 17
  • Did you ever find an answer? Thanks. – Casey Perkins Feb 12 '21 at 15:55
  • Still an issue and wasn't solve in 7.0. We made a workaround that isn't elegant. – Sid Apr 03 '21 at 16:54
  • Works fine. Xcode 13.4 / watchOS 8.5. Project from template. Just copy-pasted above code and commented `.isDetailLink(false)`. – Asperi Jun 06 '22 at 19:29
  • @Asperi you're right, confirmed. For some reason `.isDetailLink(false)` is no longer needed, at least on WatchOS. Can't believe I didn't test that myself. If you add this as an answer I can award you the bounty :) – James Allen Jun 07 '22 at 09:13
  • How do you inject the AppState from SceneDelegate when initializing the root view? – user1054922 Jun 14 '23 at 23:46

2 Answers2

0

An alternative is to (re)set the id of the NavigationView

Replace AppState with

class AppState : ObservableObject {
    @Published var appID = UUID()
}

In MoreTests remove the isActive parameter and add this modifier right before setting the navigationBarTitle

NavigationView {...

}
.id(appState.appID)

In MoreView3 assign a new UUID to the appID

Button(action: {
    self.appState.appID = UUID()
}) {
    Text("Dismiss to root")
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Interesting. What happens to the existing stack of NavigationLinks when the ID changes - are they kept alive by the system? Will this solution cause lots of orphaned views to build up over time, kind of like a memory leak? – James Allen Jun 06 '22 at 21:01
-2

Fixed with Xcode 13.4 / watchOS 8.5

  1. Create Project from template.

  2. Just copy-pasted above code

  3. Commented .isDetailLink(false)

  4. Use MoreTests as content of scene

  5. Build & Run

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Re *"the above code"*: What code are you referring to? In [the first answer](https://stackoverflow.com/questions/62207710/swiftui-how-can-i-pop-to-the-root-view-for-watchos/72521278#72521278)? In the question? – Peter Mortensen Jun 08 '22 at 14:24
  • @PeterMortensen, this is not a literature forum. Read a question, read comments, read answer, and then things come together. – Asperi Jun 08 '22 at 14:36