文章包含以下内容

  • 窗口操作
  • 如何拦截用户的窗口关闭操作
  • 如何用隐藏代替关闭窗口
  • 如何用窗口退出显示队列代替关闭
  • 如何窗口关闭后icon保持在Dock栏
  • 如何在关闭窗口时彻底杀死程序
  • 如何重新打开已关闭的窗口
  • 窗口外观
  • 大小
  • 颜色

窗口操作

1、如何拦截用户的窗口关闭操作?

首先我们要知道,在用户点击窗口左上角x关闭窗口时,系统首先调用的是



func windowShouldClose(_ sender: NSWindow) -> Bool



这个方法。该方法默认返回true,即点击关闭就销毁窗口。

我们在重写该方法,即可修改用户行为。



extension AppDelegate: NSWindowDelegate {
    func windowShouldClose(_ sender: NSWindow) -> Bool {
         NSApp.hide(nil)
         return false
    }
}



NSApp.hide(nil)将关闭行为修改为了隐藏窗口行为。

在使用该方法前,记得在func applicationDidFinishLaunching中,将window.delegate = self,这样才可以生效。

2、如何用窗口退出显示队列代替关闭?

既然方法1已经解决问题了,为什么还需要方法2呢?

方法1有个硬伤,如果使用hide代替close,在提审App Store时,会审核不通过,原因是:

Specifically, when closing the Main Window, the app stays open, however the App Menu disappears.

大家可以试一下,如果使用方法1,在用户关闭窗口时,左上角你的应用名会马上消失




windows使用swift开发并配置环境变量_ide


但苹果的要求是需要关闭窗口后,仍然保留上方的menu,直到用户点击其他窗口。

要解决这个问题,我们就需要用到方法2:


extension AppDelegate:NSWindowDelegate {
    func windowShouldClose(_ sender: NSWindow) -> Bool {
        sender.orderOut(self)
        return false
    }
}


与方法1类似,只是把hide(nil)替换为sender.orderOut(self),orderOut方法是将窗口移除显示队列,其实与hide效果类似,但是它仍然能保证menu存在。

3、如何窗口关闭后icon保持在Dock栏

我们会发现,有些应用如Foxmail在点击关闭窗口后,仍然会保留在Dock中。


windows使用swift开发并配置环境变量_sed_02


但是,有些应用关闭后马上就从Dock上消失了。

其区别就在于Plist(info.list)中的配置:


windows使用swift开发并配置环境变量_sed_03


最后一行,如果是Yes的话,则点击关闭后就不再出现在Dock中;No则会保留,并且有个小点指示器显示正在运行。

4、isReleasedWhenClosed

window?.isReleasedWhenClosed = false是一个很强大的语句。一般情况下,我们点击左上角关闭窗口,并且没有重写windowShouldClose这个方法,那么在关闭后,window这个对象就会把系统释放资源了。

但是,如果我们将isReleasedWhenClosed设为false,则窗口对象不会被销毁,我们随时可以调用其他方法将窗口重新显示出来。

5、如何点击Dock重新打开已关闭的窗口


func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
            if !flag {
                for window: AnyObject in sender.windows {
                    if window.frameAutosaveName == "Main Window" {
                    window.makeKeyAndOrderFront(self)
                    }
                }
            return true
        }
        return true
    }


我们需要重写AppDelegate中的applicationShouldHandleReopen方法,将对应的窗口放置回窗口队列的最前端。

如果isReleasedWhenClosed 没有设置为 false,则窗口对象已经被销毁,此时applicationShouldHandleReopen方法不会为你新建该window,表现出来就是点击Dock没反应。

6、如何在关闭窗口时强制杀死程序


func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        return true
    }


return true会让程序的最后一个窗口关闭时,完全杀死程序所有进程,这能确保你的程序不会再出现在Dock中。(除非你将程序固定在Dock)

设置默认窗口大小

窗口大小的设置主要在AppDelegate.swift中配置

1、通过windows.setFrame方法设置窗口大小


func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
        // Add `@Environment(.managedObjectContext)` in the views that will need the context.
        let contentView = ContentView().environment(.managedObjectContext, persistentContainer.viewContext).frame(minWidth: 450, maxWidth: .infinity, minHeight: 400, maxHeight: .infinity)
 
        // Create the window and set the content view. 
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.setFrame(CGRect(x: 10, y: 10, width: 550, height: 650), display: true)
        window.center()
        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    }


2、设置窗口最小尺寸

macos中,窗体的大小是由内容的大小决定的,所以我们只需设置内容的最小尺寸,就相当于设置了窗口的最小尺寸


let contentView = ContentView().environment(.managedObjectContext, persistentContainer.viewContext).frame(minWidth: 450, maxWidth: .infinity, minHeight: 400, maxHeight: .infinity)


3、设置窗口颜色


window.backgroundColor = NSColor.white