如何在SwiftUI Mac应用中实现窗口按行优先网格平铺?
SwiftUI Mac版多窗口行优先网格平铺实现方案
核心平铺逻辑实现
先封装一个全局的窗口平铺工具方法,基于NSApp的窗口列表计算每个窗口的位置和尺寸:
import SwiftUI extension NSApplication { static func tileWindowsInGrid() { // 获取主屏幕可用区域(排除菜单栏和Dock) guard let screenFrame = NSApp.mainWindow?.screen?.visibleFrame else { return } // 筛选可见且未最小化的窗口(可根据需求排除主窗口) let targetWindows = NSApp.windows.filter { $0.isVisible && !$0.isMiniaturized // 可选:排除主窗口 // && $0.identifier?.rawValue != "main" } guard !targetWindows.isEmpty else { return } // 自定义窗口最小尺寸(按需调整) let minWindowWidth: CGFloat = 300 let minWindowHeight: CGFloat = 400 // 计算每行最大窗口数 let maxColumns = Int(screenFrame.width / minWindowWidth) let totalWindows = targetWindows.count let columns = min(maxColumns, totalWindows) let rows = (totalWindows + columns - 1) / columns // 向上取整计算行数 // 计算每个窗口的实际大小(铺满屏幕可用区域) let windowWidth = screenFrame.width / CGFloat(columns) let windowHeight = screenFrame.height / CGFloat(rows) // 逐个设置窗口位置(Mac坐标系原点在屏幕左下角) for (index, window) in targetWindows.enumerated() { let column = index % columns let row = index / columns let originX = screenFrame.origin.x + CGFloat(column) * windowWidth let originY = screenFrame.origin.y + screenFrame.height - CGFloat(row + 1) * windowHeight window.setFrame(NSRect(x: originX, y: originY, width: windowWidth, height: windowHeight), display: true) } } }
1. 创建窗口时自动平铺
在触发新窗口创建的逻辑后,延迟调用平铺方法(确保新窗口已加入窗口列表):
// 示例:打开新子游戏窗口的按钮 Button("添加新游戏窗口") { // 打开指定ID的窗口 NSApp.openWindow(id: "wordle-subgame") // 延迟执行平铺,确保窗口初始化完成 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { NSApplication.tileWindowsInGrid() } }
2. 添加「平铺窗口」菜单选项
在App结构体中通过CommandMenu扩展系统「窗口」菜单:
@main struct WordleHelperApp: App { var body: some Scene { WindowGroup(id: "main") { MainView() } WindowGroup(id: "wordle-subgame") { WordleSubgameView() } // 扩展窗口菜单 CommandMenu("窗口") { Button("平铺窗口") { NSApplication.tileWindowsInGrid() } .keyboardShortcut("T", modifiers: [.command, .shift]) // 可选:添加快捷键 } } }
可选优化点
- 可根据窗口数量动态调整列数(比如4个窗口用2列,8个用3列),替代按最小宽度计算的逻辑
- 支持保留窗口的最小尺寸限制,当屏幕空间不足时自动滚动
- 可以记忆用户调整后的窗口布局,避免每次平铺都强制覆盖
内容的提问来源于stack exchange,提问作者Duncan C
相关产品推荐
相关产品推荐

