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

加载时设置窗口高度:最大化失效及弱引用警告问题排查

问题修复方案

1. 解决代理被立即释放的警告

NSWindow的delegate是弱引用属性,你之前直接创建临时WindowController实例赋值,因为没有强引用持有它,系统会立刻回收这个实例,所以才会出现警告。必须把WindowController实例通过强引用存起来,比如在视图里用@StateObject。

2. 让窗口尺寸设置生效

你之前对visibleFrame的处理多此一举了——NSScreen.main?.visibleFrame返回的本来就是排除了菜单栏和Dock的屏幕可用区域,不需要再手动调整高度。另外得让窗口控制器真正关联到SwiftUI的窗口,不能随便创建临时实例瞎赋值。

完整修正代码

import AppKit
import SwiftUI

@main
struct demoApp: App {
    var body: some Scene {
        WindowGroup ("Demo") {
            ContentView()
        }
    }
}

class WindowController: NSWindowController, NSWindowDelegate {
    override func windowDidLoad() {
        super.windowDidLoad()
        guard let screenFrame = NSScreen.main?.visibleFrame else { return }
        
        // visibleFrame已经是去掉菜单栏和Dock的可用区域,直接用它设窗口大小
        window?.setFrame(screenFrame, display: true, animate: false)
        window?.delegate = self
    }
    
    // 如果要限制窗口调整大小时的最大高度,保留这个方法(按需调整)
    func windowWillResize(_ sender: NSWindow, to frameSize: NSSize) -> NSSize {
        guard let screenFrame = NSScreen.main?.visibleFrame else { return frameSize }
        let maxHeight = screenFrame.height
        return NSSize(width: frameSize.width, height: min(frameSize.height, maxHeight))
    }
}

struct ContentView: View {
    // 用@StateObject强持有WindowController实例,防止被系统回收
    @StateObject private var windowController = WindowController()
    
    var body: some View {
        Text("Hello, World!")
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.blue)
            .edgesIgnoringSafeArea(.all)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Title")
                        .font(.headline)
                }
            }
            // 嵌入WindowAccessor,把SwiftUI的窗口和我们的控制器绑定
            .background(WindowAccessor(windowController: windowController))
    }
}

struct WindowAccessor: NSViewRepresentable {
    let windowController: WindowController
    
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            if let window = view.window {
                // 把窗口交给已持有的控制器管理
                windowController.window = window
                windowController.windowDidLoad()
            }
        }
        return view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {}
}

关键修改说明

  • 用@StateObject在ContentView里持有WindowController实例,确保系统不会随便释放它,彻底解决代理被立即回收的警告。
  • 修正窗口尺寸计算:直接用visibleFrame设置窗口,它已经自动排除了菜单栏和Dock的占用,之前手动调整高度的操作反而导致设置失效。
  • 通过WindowAccessor把SwiftUI的窗口和我们持有的控制器绑定,让控制器能真正控制窗口的行为。
  • 删除了onAppear里那种临时创建控制器赋值的错误代码,那是导致代理警告的根源。

改完之后,窗口每次启动都会自动占满可用屏幕区域,两个问题都能解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:10:10