WWDC Quick Look 💓 By SwiftGGTeam
Tailor macOS windows with SwiftUI

Tailor macOS windows with SwiftUI

观看原视频

Highlight

SwiftUI 在 macOS Sequoia 新增了 .toolbarBackgroundVisibility().windowMinimizeBehavior().defaultWindowPlacement() 等窗口定制 API,无需 NSWindow bridging 即可实现隐藏标题栏、禁用最小化按钮、动态计算窗口位置等功能(02:02)。


核心内容

Session 以 Destination Video 示例 App 展示如何为不同类型的窗口定制外观和行为。开发者面临的问题是:SwiftUI 的默认窗口样式不够灵活——视频播放器需要根据内容动态计算尺寸,About 窗口不需要最小化按钮,主窗口希望内容延伸到顶部边缘。这些需求之前都需要写 NSWindow 的 Objective-C bridging 代码。

Apple 在 macOS Sequoia 中引入了三类新 API:Toolbar 定制可隐藏标题和工具栏背景、窗口行为控制可禁用最小化按钮和状态恢复、窗口位置控制可根据内容尺寸和屏幕尺寸动态计算窗口的理想位置。


详细内容

Toolbar 定制

Destination Video 的主窗口有一个大图需要延伸到窗口顶部边缘。默认情况下,toolbar 和标题区域会占据顶部空间(03:00)。

WindowGroup("Destination Video", id: "main") {
    MainView()
        .toolbar(removing: .title)
        .toolbarBackgroundVisibility(.hidden, for: .windowToolbar)
}
  • .toolbar(removing: .title) 隐藏窗口标题栏(标题仍保留在窗口元数据中,主菜单和无障碍功能会继续使用)
  • .toolbarBackgroundVisibility(.hidden, for: .windowToolbar) 移除工具栏背景,让内容延伸到窗口顶部边缘

窗口行为控制

About 窗口显示静态信息,从主菜单随时可达,不需要最小化按钮和状态恢复(05:12)。

Window("About Destination Video", id: "about") {
    AboutView()
        .toolbar(removing: .title)
        .toolbarBackgroundVisibility(.hidden, for: .windowToolbar)
        .containerBackground(.thickMaterial, for: .window)
        .windowMinimizeBehavior(.disabled)
        .restorationBehavior(.disabled)
}
  • .containerBackground(.thickMaterial, for: .window) 用毛玻璃材质替换默认背景颜色
  • .windowMinimizeBehavior(.disabled) 禁用黄色最小化按钮
  • .restorationBehavior(.disabled) 关闭状态恢复(App 退出后不会自动重开此窗口)

窗口位置控制

视频播放器窗口需要根据视频尺寸和屏幕尺寸动态计算位置和大小(07:02)。

Window("Video Player", id: "player") {
    PlayerView(video: video)
        .defaultWindowPlacement { content, context in
            let idealSize = content.sizeThatFits(.unspecified)
            let visibleRect = context.display.visibleRect

            let size = idealSize.clamped(to: visibleRect.size)
            return .default(.center, size: size)
        }
        .windowIdealPlacement { content, context in
            let visibleRect = context.display.visibleRect
            let zoomedSize = zoomToFit(content.sizeThatFits(.unspecified), in: visibleRect.size)
            return .default(.center, size: zoomedSize)
        }
}
  • .defaultWindowPlacement() 设置新窗口的初始位置和尺寸
    • content.sizeThatFits() 计算内容理想尺寸
    • context.display.visibleRect 获取屏幕可用区域(自动扣除菜单栏和 Dock 占用空间)
    • .clamped(to:) 确保窗口不超出屏幕边界
  • .windowIdealPlacement() 控制用户点击 Zoom 按钮后的窗口尺寸
    • zoomToFit() 自定义函数,在保持宽高比前提下最大化窗口

关键点:

  • 视频以原生尺寸显示,超出屏幕时自动缩小适应
  • 窗口默认居中显示
  • Zoom 行为可自定义,不再受系统默认逻辑限制

核心启发

1. 为每种窗口类型定制独立配置

主窗口、About 窗口、设置窗口、播放器窗口的 toolbar 需求、最小化行为、状态恢复策略都应该不同。花 10 分钟为每种窗口添加合适的 modifier,用户体验会明显提升。

2. 优先迁移用户最易感知的改动

.toolbarBackgroundVisibility(.hidden) 让内容布局更自由,.defaultWindowPlacement() 修正初始窗口大小。这两个改动迁移成本最低,用户感知最强。状态恢复和最小化行为的修改更谨慎,因为它们改变了用户已有的操作习惯。

3. 利用新 API 避免跨语言桥接

之前需要写 NSWindow 的 Objective-C bridging 代码实现的需求,现在 SwiftUI 都有原生的 modifier。新项目优先用 SwiftUI API,维护成本更低。


关联 Session

评论

GitHub Issues · utterances