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

如何重新打开窗口并恢复其关闭时的精确状态?

问题描述

我需要实现重新打开窗口时恢复其关闭时的精确状态(用于展示中间的沉浸式空间),但直接调用dismissWindow(id:)和openWindow(id:)会创建窗口的新实例,无法保留原有状态。尝试过两种方案,但都存在缺陷:

  • 保存状态并传入openWindow:主信息流页面包含动态滚动视图,读取和设置滚动偏移量等操作会引发诸多问题。
  • 通过WindowGroup { TabView {}.opacity(showWindow ? 1 : 0) }隐藏/显示窗口:系统窗口栏和关闭按钮仍然可见,达不到沉浸式效果。

可行解决方案

1. 用SceneStorage持久化窗口核心状态(含滚动位置)

对于SwiftUI的滚动视图,结合ScrollViewReader和SceneStorage可以精准保存并恢复滚动位置,同时持久化其他视图状态(如选中的Tab、展开的单元格等)。SceneStorage会自动关联窗口ID,重新打开同ID窗口时自动恢复状态。

示例代码:

struct MainFeedView: View {
    // 持久化滚动位置的元素ID
    @SceneStorage("mainFeedScrollPosition") private var scrollPosition: String?
    // 持久化选中的Tab索引
    @SceneStorage("selectedTabIndex") private var selectedTabIndex = 0
    
    var body: some View {
        TabView(selection: $selectedTabIndex) {
            ScrollView {
                ScrollViewReader { proxy in
                    LazyVStack {
                        ForEach(0..<100) { index in
                            Text("Feed Item \(index)")
                                .id("item-\(index)")
                                .frame(height: 80)
                                .background(Color.gray.opacity(0.2))
                        }
                    }
                    .onAppear {
                        // 恢复滚动位置
                        if let scrollPosition = scrollPosition {
                            proxy.scrollTo(scrollPosition, anchor: .top)
                        }
                    }
                }
            }
            .tag(0)
            
            Text("Second Tab")
                .tag(1)
        }
        // 监听滚动,保存当前可见区域的核心元素ID
        .onReceive(NotificationCenter.default.publisher(for: UIScrollView.didScrollNotification)) { notification in
            guard let scrollView = notification.object as? UIScrollView else { return }
            let visibleCenter = CGPoint(
                x: scrollView.contentOffset.x + scrollView.bounds.midX,
                y: scrollView.contentOffset.y + scrollView.bounds.midY
            )
            
            if let indexPath = scrollView.indexPathForRow(at: visibleCenter) {
                scrollPosition = "item-\(indexPath.row)"
            }
        }
    }
}

2. 不销毁窗口,通过调整窗口状态隐藏(macOS)

如果是macOS应用,直接操作NSWindow实例,将窗口最小化或移出屏幕,而非调用dismissWindow,这样窗口实例完全保留,状态不会丢失。

示例代码:

import SwiftUI

struct MainWindowView: View {
    @Environment(\.openWindow) private var openWindow
    
    var body: some View {
        Button("切换到沉浸式空间") {
            // 根据窗口ID定位目标窗口
            if let window = NSApplication.shared.windows.first(where: { 
                $0.identifier?.rawValue == "MainWindow" 
            }) {
                // 最小化窗口,保留实例
                window.miniaturize(nil)
                // 也可以将窗口移到屏幕外:
                // window.setFrame(CGRect(x: -1000, y: -1000, width: window.frame.width, height: window.frame.height), display: false)
            }
            // 打开沉浸式空间窗口
            openWindow(id: "ImmersiveSpace")
        }
    }
}

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup(id: "MainWindow") {
            MainWindowView()
        }
        WindowGroup(id: "ImmersiveSpace") {
            ImmersiveSpaceView()
        }
    }
}

3. 自定义窗口样式隐藏系统元素(iOS/macOS)

如果必须用隐藏/显示的方式,可通过自定义窗口样式隐藏系统栏和按钮,配合透明度实现完全隐藏,同时保留窗口实例。

iOS/iPadOS 示例:

struct MainFeedView: View {
    @Binding var showWindow: Bool
    
    var body: some View {
        TabView {
            // 主信息流内容
        }
        .toolbar(.hidden, for: .navigationBar)
        .statusBarHidden(!showWindow)
        .opacity(showWindow ? 1 : 0)
        .disabled(!showWindow)
    }
}

macOS 示例:

struct MainFeedView: View {
    @Binding var showWindow: Bool
    
    var body: some View {
        TabView {
            // 主信息流内容
        }
        .frame(minWidth: 800, minHeight: 600)
        .toolbar(.hidden, for: .windowToolbar)
        .opacity(showWindow ? 1 : 0)
        .onChange(of: showWindow) { newValue in
            guard let window = NSApplication.shared.windows.first(where: { 
                $0.contentView?.subviews.contains(where: { $0 is NSHostingView }) ?? false 
            }) else { return }
            // 隐藏/显示系统窗口按钮
            window.standardWindowButton(.closeButton)?.isHidden = !newValue
            window.standardWindowButton(.miniaturizeButton)?.isHidden = !newValue
            window.standardWindowButton(.zoomButton)?.isHidden = !newValue
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 08:25:04