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

如何让SwiftUI的NavigationSplitView保留详情视图的State?

如何让SwiftUI的NavigationSplitView切换详情视图时保留状态?

你遇到的问题本质是:每次切换侧边栏选择时,原来的详情视图会被销毁重建,导致所有关联的状态(滚动位置、文本选择、内部State变量)全部丢失。而TabView默认会缓存视图实例,所以不会出现这个问题。

解决方案:保留视图实例,避免销毁重建

核心思路是让每个面板的视图实例在内存中持续存活,只是根据选择状态显示或隐藏,而不是每次切换都重新创建。

方案1:用@State存储视图实例(简单场景)

修改DetailColumn,把每个面板的视图存在@State变量中,确保实例不会被销毁:

struct DetailColumn: View {
    @Binding var selection: Panel?
    // 预存每个面板的视图实例,保持内存中存活
    @State private var panelOneView = BigList()
    @State private var panelTwoView = BigList()
    
    var body: some View {
        Group {
            if selection == .one {
                panelOneView
            } else {
                panelTwoView
            }
        }
    }
}

方案2:用可观察对象管理面板(规范场景,iOS17+/macOS14+)

如果面板数量较多或需要更集中的状态管理,用@Observable类存储所有面板实例:

import SwiftUI
import Observation

enum Panel: Hashable {
    case one
    case two
}

struct BigList: View {
    var body: some View {
        List {
            ForEach(1...500, id: \.self) { value in
                Text("\(value)")
            }
        }
        .textSelection(.enabled)
    }
}

@Observable
class PanelManager {
    var selectedPanel: Panel = .one
    // 预创建所有面板的视图实例
    let panelOne = BigList()
    let panelTwo = BigList()
}

struct DetailColumn: View {
    let panelManager: PanelManager
    
    var body: some View {
        Group {
            if panelManager.selectedPanel == .one {
                panelManager.panelOne
            } else {
                panelManager.panelTwo
            }
        }
    }
}

@main
struct TestSplitViewApp: App {
    @State private var panelManager = PanelManager()
    @State private var path = NavigationPath()

    var body: some Scene {
        WindowGroup {
            NavigationSplitView {
                List(selection: $panelManager.selectedPanel) {
                    NavigationLink(value: Panel.one) {
                        Label("One", systemImage: "circle")
                    }
                    NavigationLink(value: Panel.two) {
                        Label("Two", systemImage: "circle")
                    }
                }
            } detail: {
                NavigationStack(path: $path) {
                    DetailColumn(panelManager: panelManager)
                }
            }
            .onChange(of: panelManager.selectedPanel) { _ in
                path.removeLast(path.count)
            }
        }
    }
}

原理说明

  • 原代码中switch selection会在每次切换时销毁旧的BigList实例,创建新实例,所有状态自然丢失。
  • 修改后,视图实例在初始化时就被创建并保存在@State或可观察对象中,切换时只是改变显示/隐藏状态,实例本身不会被销毁,因此滚动位置、文本选择、内部State都会被完整保留。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 11:06:36