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

基于NSWindowController与NSHostingView的contentView:帧尺寸更新延迟问题

macOS中NSHostingView尺寸更新延迟问题

我在macOS应用中搭建了如下简化示例,测试SwiftUI视图尺寸更新后NSWindow内容视图的同步行为:

代码示例

AppDelegate.swift

import Cocoa

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        TestWindow()
    }

    func applicationWillTerminate(_ aNotification: Notification) {}

    func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
        return true
    }
}

BareView.swift

import SwiftUI

struct BareView: View {
    @ObservedObject var testState: TestState
    
    var body: some View {
        Color.white.frame(width: testState.width, height: testState.height)
    }
}

TestWindow.swift

import Foundation
import AppKit
import SwiftUI

class TestState: ObservableObject {
    @Published var width: CGFloat = 100
    @Published var height: CGFloat = 100
}

class TestWindow: NSWindowController {
    let testState = TestState()

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    init() {
        super.init(window: NSWindow(
            contentRect: .zero,
            styleMask: [ .borderless ],
            backing: .buffered,
            defer: false))
        
        self.window!.contentView = NSHostingView(rootView: BareView(testState: testState))
        
        self.window!.setFrameOrigin(.init(x: NSScreen.main!.frame.width / 2, y: NSScreen.main!.frame.height / 2))
        self.window!.makeKeyAndOrderFront(nil)
        
        NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) { event in
            self.testState.width += 10
            self.testState.height += 10
            self.window!.makeKeyAndOrderFront(nil)
            // 取消注释下方代码才能得到预期行为:
            // self.window!.setContentSize(.init(width: self.testState.width, height: self.testState.height))
            print("contentSize: \(self.testState.width)")
            print("viewFrame: \(self.window!.contentView!.frame)")
            return nil
        }
    }
}

问题现象

通过鼠标移动事件更新TestState的宽高时,SwiftUI视图已完成尺寸增大,但NSWindow的contentView帧尺寸并未即时同步,而是延迟一轮更新(需等窗口移动后才会同步)。此时控制台输出如下:

contentSize: 110.0
viewFrame: (0.0, 0.0, 100.0, 100.0)
contentSize: 120.0
viewFrame: (0.0, 0.0, 110.0, 110.0)

若手动调用setContentSize(_:)更新窗口内容尺寸,控制台输出则符合预期:

contentSize: 110.0
viewFrame: (0.0, 0.0, 110.0, 110.0)
contentSize: 120.0
viewFrame: (0.0, 0.0, 120.0, 120.0)

看起来SwiftUI的视图尺寸更新会等到当前代码执行完毕后才触发窗口尺寸重新计算,导致SwiftUI视图已变大但窗口未感知,进而引发原点无效的问题。我可以通过手动调用setContentSize(_:)解决,但尺寸信息往往位于视图树深层的叶子视图中,需要借助ObservableObject将计算后的尺寸传递回NSWindowController。

疑问

请问这是否属于预期行为?背后的窗口重绘机制是怎样的?

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 00:31:03