在 SwiftUI 中处理嵌套导航堆栈?

问题描述 投票:0回答:1

我环顾四周,但没有看到任何这方面的信息。如果我想在 SwiftUI 中拥有多个导航堆栈,并拥有多个添加到不同导航堆栈的导航链接,我该怎么做?在我的用例中,我使用选项卡视图,所以我会是这样的:

NavigationStack(path: $globalNavigationStack) {
  TabView {
    PrimaryView()
    SecondaryView()
  }
}

然后也许在 PrimaryView 中:

struct PrimaryView: View {
  var body: some View {
    NavigationStack(path: $localNavigationStack) {
       NavigationLink(value: SomeType.self) {
         Button("This should add to local stack")
       }
       .navigationDestination(for: SomeType.val) { someType in
          ViewOnLocalStack() 
       }

       NavigationLink(value: SomeType.self) {
         Button("This should add to global stack")
       }
       .navigationDestination(for: SomeType.val) { someType in
          ViewOnGlobalStack() 
       }
    }
  }
}

但是第二个

NavigationLink
将添加到本地堆栈中。假设我有权访问全局路径,如何使第二个路径添加到全局路径?我尝试的是这样的:

NavigationStack(path: $globalNavigationStack) {
  TabView {
    PrimaryView()
    SecondaryView()
  }
}
.navigationDestination(for: SomeType.val) { someType in
    ViewOnGlobalStack() 
}

同时从第二个链接中删除

onDestination
,但控制台抱怨导航链接没有相应的
onDestination
。不知道是否见过其他人有类似的情况。

swiftui swiftui-navigationstack
1个回答
0
投票

TLDR; 嵌套

NavigationStack
是不可能的。您将需要一种混合方法。以下答案是关于如何在不可能嵌套的情况下处理它的建议。

如果你尝试的话,XCode 不一定会抱怨。但即使你能够让它工作,你的应用程序也会很快崩溃。

我的建议:不要尝试破解它。

NavigationStack
能够很好地管理一层深度的导航状态。要在视图状态中创建细微差别,您要么必须创建大量具有隔离状态的视图,要么创建较少的具有共享状态的稳健视图。

这是树结构中不好的方法与一些建议的方法。请注意,我使用术语“屏幕”,因为视图在 SwiftUI 中具有特定含义。

您目前如何想象您的应用程序导航方案:

不好的方法

App <- NavigationStack
│
├── Screen1 <- NavigationStack
│   ├── Subscreen1A
│   └── Subscreen1B
│
└── Screen2 <- NavigationStack
    ├── Subscreen2A
    └── Subscreen2B

更好的方法:您的根级别包含 NavigationStack 和自定义全局状态对象。

App <- NavigationStack and global state object for subviews.
│
├── Screen1 <- reacts to global state object for changes
├── Screen2 <- reacts to global state object for changes

更复杂的方法:屏幕不共享状态。

App <- NavigationStack
│
├── Screen1 <- custom state object with conditionally rendered subviews
├── Screen2 <- custom state object with conditionally rendered subviews

因此,我将记录有关如何处理子视图的几点指导:

  • 为复杂视图创建状态对象:如果状态必须在视图销毁后持续存在,则将其放入父视图或全局状态中。
  • 使用布尔值或枚举有条件地渲染子视图:除了初始
    NavigationStack
    之外,使用这些数据类型来确定如何渲染整个视图或部分视图。
  • 避免“视图模型”抽象:由于 SwiftUI 管理视图生命周期的方式,它根本行不通。
  • 如果可能,避免使用 UIKit:如果您发现自己需要在应用程序中包含 UIKit 和 SwiftUI,请准备好接受大量的粘合代码。即使您不直接使用 UIKit,受 UIKit 启发的建议也可能会产生误导。
  • 尽早且经常进行抽象:简洁并不是我认为 SwiftUI 的品质(当然它比 UIKit 更好,但痛苦就是痛苦)。封装明显的职责(例如无状态逻辑或数据结构)。

最后,这里有一个例子来说明我之前提到的“更好的方法”。它会让你开始假设全局状态现在有效。

import SwiftUI

enum AppRoute: Hashable {
  case screenOne
  case screenTwo
  case screenThree
}

final class AppState: ObservableObject {
  @Published var path = NavigationPath()
  // Can't be optional.
  // Manage in parent to inject state into "child" views
  @Published var screenTwoData = ?
  @Published var isScreenTwoFlowActive: Bool = false
}

@main

struct MyApp: App {
  @StateObject private var appState = AppState()

  var body: some Scene {
    WindowGroup {
      NavigationStack(path: $appState.path) {
        VStack {
          ScreenOne()
        }
        .navigationDestination(for: AppRoute.self) { appRoute in
          switch appRoute {
          case .screenOne:
            ScreenTwo()
          case .screenTwo:
            ScreenThree()
          }
        }
      }
      .environmentObject(appState)
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.