如何在Composable Architecture中使用NavigationSplitView?
核心问题分析
你当前的代码混淆了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
相关产品推荐
相关产品推荐

