Nik*_*nko 4 macos swift swiftui
如何在MacOS 桌面上的 SwiftUI 中设置窗口坐标?例如,窗口应该始终出现在中心还是始终出现在右上角?
这是我的版本,但是,我移动代码并关闭它,当我打开它时,它首先出现在旧位置,然后跳转到新位置。
import SwiftUI
let WIDTH: CGFloat = 400
let HEIGTH: CGFloat = 200
@main
struct ForVSCode_MacOSApp: App {
@State var window : NSWindow?
var body: some Scene {
WindowGroup {
ContentView(win: $window)
}
}
}
struct WindowAccessor: NSViewRepresentable{
@Binding var window: NSWindow?
func makeNSView(context: Context) -> some NSView {
let view = NSView()
let width = (NSScreen.main?.frame.width)!
let heigth = (NSScreen.main?.frame.height)!
let resWidth: CGFloat = (width / 2) - (WIDTH / 2)
let resHeigt: CGFloat = (heigth / 2) - (HEIGTH / 2)
DispatchQueue.main.async {
self.window = view.window
self.window?.setFrameOrigin(NSPoint(x: resWidth, y: resHeigt))
self.window?.setFrameAutosaveName("mainWindow")
self.window?.isReleasedWhenClosed = false
self.window?.makeKeyAndOrderFront(nil)
}
return view
}
func updateNSView(_ nsView: NSViewType, context: Context) {
}
}
Run Code Online (Sandbox Code Playgroud)
和内容视图
import SwiftUI
struct ContentView: View {
@Binding var win: NSWindow?
var body: some View {
VStack {
Text("it finally works!")
}
.font(.largeTitle)
.frame(width: WIDTH, height: HEIGTH, alignment: .center)
.background(WindowAccessor(window: $win))
}
}
struct ContentView_Previews: PreviewProvider {
@Binding var win: NSWindow?
static var previews: some View {
ContentView(win: .constant(NSWindow()))
.frame(width: 250, height: 150, alignment: .center)
}
}
Run Code Online (Sandbox Code Playgroud)
我在我的一个项目中确实遇到了同样的问题,我想我会更深入地研究一下,我发现了两种控制窗口位置的方法。
因此,我影响窗口位置的第一个方法是预先定义窗口在屏幕上的最后位置。
当应用程序的第一个窗口打开时,macOS 将尝试恢复上次关闭时的最后一个窗口位置。为了区分不同的窗口,每个窗口都有自己的frameAutosaveName. 窗口框架会自动以文本格式保留在应用程序首选项 ( UserDefaults.standard) 中,其密钥源自frameAutosaveName:“NSWindow Frame <frameAutosaveName>”(请参阅 saveFrame 的文档)。
如果您未在 中指定 ID WindowGroup,SwiftUI 将从您的主视图类名称中派生自动保存名称。前三个窗口将具有以下自动保存名称:
<ModuleName>.ContentView-1-AppWindow-1
<ModuleName>.ContentView-1-AppWindow-2
<ModuleName>.ContentView-1-AppWindow-3
Run Code Online (Sandbox Code Playgroud)
例如,通过设置 ID WindowGroup(id: "main"),将使用以下自动保存名称(同样适用于前三个窗口):
main-AppWindow-1
main-AppWindow-2
main-AppWindow-3
Run Code Online (Sandbox Code Playgroud)
当您检查应用程序首选项目录(UserDefaults.standard存储位置)时,您将在 plist 中看到一项:
NSWindow Frame main-AppWindow-1 1304 545 400 228 0 0 3008 1228
Run Code Online (Sandbox Code Playgroud)
有很多数字需要消化。前 4 个整数描述窗口框架(原点和大小),接下来的 4 个整数描述屏幕框架。
手动设置这些值时需要记住以下几点:
因此,为了伪造屏幕中心的初始位置,我使用了在应用程序(或 ContentView)初始化程序中运行的以下函数。但请记住:使用此方法只有第一个窗口会居中。所有后续窗口都将被放置在前一个窗口的右侧。
<ModuleName>.ContentView-1-AppWindow-1
<ModuleName>.ContentView-1-AppWindow-2
<ModuleName>.ContentView-1-AppWindow-3
Run Code Online (Sandbox Code Playgroud)
我的第二种方法是直接操作NSWindow类似于您的底层WindowAccessor。
您的实现WindowAccessor有一个特定的缺陷:正在读取view.window以提取NSWindow实例的块是异步运行的:在将来的某个时间(由于DispatchQueue.main.async)。
这就是为什么窗口出现在屏幕上的 SwiftUI 配置位置,然后再次消失,最终移动到您想要的位置。您需要更多的控制,这包括首先监视属性设置NSView后尽快获得通知,然后监视实例以了解视图何时变得可见。windowNSWindow
我正在使用以下WindowAccessor. 它需要一个onChange回调闭包,每当发生变化时就会调用该回调闭包window。首先,它开始监视NSViewswindow属性,以便在视图添加到窗口时获得通知。发生这种情况时,它开始监听NSWindow.willCloseNotification通知以检测窗口何时关闭。此时它将停止任何监视以避免内存泄漏。
main-AppWindow-1
main-AppWindow-2
main-AppWindow-3
Run Code Online (Sandbox Code Playgroud)
NSWindow然后可以使用此实现来尽快获取句柄。我们仍然面临的问题:我们无法完全控制窗口。我们只是监视发生的情况并可以与NSWindow实例交互。这意味着:我们可以设置位置,但我们不确切知道这应该在哪一刻发生。例如,在将视图添加到窗口后直接设置窗口框架不会产生任何影响,因为 SwiftUI 首先进行布局计算,然后决定将窗口放置在哪里。
经过一番摸索后,我开始追踪该NSWindow.isVisible房产。这允许我在窗口可见时设置位置。使用上面WindowAccessor我的ContentView实现如下所示:
NSWindow Frame main-AppWindow-1 1304 545 400 228 0 0 3008 1228
Run Code Online (Sandbox Code Playgroud)
我希望这个解决方案可以帮助其他想要在 SwiftUI 中获得更多窗口控制的人。
| 归档时间: |
|
| 查看次数: |
2893 次 |
| 最近记录: |