You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Composable Architecture中使用NavigationSplitView?

在Composable Architecture中正确使用NavigationSplitView实现macOS侧边栏导航

核心问题分析

你当前的代码混淆了NavigationStackStore与NavigationSplitView的逻辑。NavigationSplitView通过选中状态绑定控制详情区内容,而非依赖NavigationStack的destination回调。需要调整ProjectsFeature的状态管理逻辑,配合NavigationSplitView的原生交互实现需求。

步骤1:更新ProjectsFeature的状态与逻辑

在根Feature中添加选中项目的状态,并处理选中事件:

struct ProjectsFeature: Reducer {
  struct State: Equatable {
    var projects: [Project]
    // 新增:记录选中的项目
    var selectedProject: Project?
  }
  
  enum Action {
    // 新增:处理项目选中事件
    case selectProject(Project)
  }
  
  func reduce(into state: inout State, action: Action) -> Effect<Action> {
    switch action {
    case .selectProject(let project):
      state.selectedProject = project
      return .none
    }
  }
}

步骤2:重构ProjectsView布局

移除外层的NavigationStackStore(无需详情页内深层导航时),直接通过NavigationSplitView结合选中状态渲染详情区:

struct ProjectsView: View {
  let store: StoreOf<ProjectsFeature>
  
  var body: some View {
    WithViewStore(store, observe: { $0 }) { viewStore in
      NavigationSplitView {
        // 通过selection绑定选中状态,触发根Feature的选中事件
        List(selection: viewStore.binding(get: \.selectedProject, send: ProjectsFeature.Action.selectProject)) {
          ForEach(viewStore.projects) { project in
            Text(project.name)
              .tag(project)
          }
        }
      } detail: {
        // 根据选中状态渲染对应ProjectView
        if let selectedProject = viewStore.selectedProject {
          ProjectView(
            store: store.scope(
              state: { _ in ProjectFeature.State(project: selectedProject) },
              action: { _ in .none }
            )
          )
        } else {
          Text("请选择一个项目")
        }
      }
    }
  }
}

步骤3:支持详情页内深层导航(可选)

如果需要在ProjectView内继续嵌套导航,可以在详情区添加NavigationStackStore:

// 先更新ProjectsFeature的状态与逻辑
struct ProjectsFeature: Reducer {
  struct State: Equatable {
    var projects: [Project]
    var selectedProject: Project?
    // 新增:详情页的导航路径
    var detailPath = StackState<ProjectDetailFeature.State>()
  }
  
  enum Action {
    case selectProject(Project)
    case detailPath(StackAction<ProjectDetailFeature.State, ProjectDetailFeature.Action>)
  }
  
  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .selectProject(let project):
        state.selectedProject = project
        return .none
      case .detailPath:
        return .none
      }
    }
    .forEach(\.detailPath, action: /Action.detailPath) {
      ProjectDetailFeature()
    }
  }
}

// 重构详情区的导航结构
struct ProjectsView: View {
  let store: StoreOf<ProjectsFeature>
  
  var body: some View {
    WithViewStore(store, observe: { $0 }) { viewStore in
      NavigationSplitView {
        List(selection: viewStore.binding(get: \.selectedProject, send: ProjectsFeature.Action.selectProject)) {
          ForEach(viewStore.projects) { project in
            Text(project.name)
              .tag(project)
          }
        }
      } detail: {
        if let selectedProject = viewStore.selectedProject {
          NavigationStackStore(store.scope(state: \.detailPath, action: ProjectsFeature.Action.detailPath)) {
            ProjectView(
              store: store.scope(
                state: { _ in ProjectFeature.State(project: selectedProject) },
                action: { _ in .none }
              )
            )
          } destination: { detailStore in
            ProjectDetailView(store: detailStore)
          }
        } else {
          Text("请选择一个项目")
        }
      }
    }
  }
}

关键说明

  • NavigationSplitView的侧边栏通过List(selection:)绑定选中状态,直接触发根Feature的action更新选中项目
  • 详情区根据选中状态,通过scope将根Feature的状态转换为ProjectFeature所需状态,创建对应视图
  • 无需深层导航时,完全不需要NavigationStackStore,避免逻辑混淆

内容的提问来源于stack exchange,提问作者eivindml

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 01:54:50