窗口概述
窗口的类型
窗口是View视图的容器,而VIew视图是UI控件的容器。窗口也负责接收用户的鼠标键盘等系统事件,然后负责转发消息到相应的接收对象。在Appkit中有很多window的子类,可以提供更多功能,提供更好友好的交互。Mac OS的窗口中一共有以下几种不同的状态/分类:
- deactive window:多个app同时打开时,只有一个可以是active状态,其它全是deactive状态;
- active window:多个app同时打开时,当前可操作的app;
- key window:当前active window可操作的窗口,一般用于接收系统事件;
- main window:当前active window可操作的窗口,main window也可以是key window;

有一个控件需要注意,NSPanel 为面板对象,它只能是main window而不能是key window。
窗口的组成

在XIB文件导航中选择windows对象即可对window进行个性化的配置,就是上节课所说的右侧的8个inspector面板,比如在 size inspector 面板中 content border 选择可以设置其bottom是否显示,默认为none不显示;
模态窗口
其实就是传统所说的Dialog,这种窗口弹出后除非手动关闭,否则只能在这个窗口中操作。在Mac OS中分为两种:
- modal window:弹出显示,独占;
- session window:弹出显示,不独占;

设计实现
- 在xib中添加一个NSWindow对象,连接到AppDelegate.swift文件中
- 添加一个动作按钮,绑定IBAction事件;
- 添加Observer,在关闭时结束窗口模态事件(因为此时是设置了整体的状态,不取消状态会导致整个App不能再操作);
代码实现如下:
import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet var window: NSWindow!
@IBOutlet weak var modalWindow: NSWindow!
var sessionCode : NSApplication.ModalSession?
func applicationDidFinishLaunching(_ aNotification: Notification) {
// 这个侦听器对modal 和 sessions 同时生效必须要写
NotificationCenter.default.addObserver(self, selector: #selector(self.modalWindowClose(_:)), name: NSWindow.willCloseNotification, object: nil)
}
func applicationWillTerminate(_ aNotification: Notification) {
NotificationCenter.default.removeObserver(self)
}
//显示modal窗口
@IBAction func showModelWindow(_ sender: NSButton) {
NSApplication.shared.runModal(for: self.modalWindow)
self.modalWindow.center()
}
//显示session窗口
@IBAction func showSessionWindow(_ sender: NSButton) {
sessionCode = NSApplication.shared.beginModalSession(for: self.modalWindow)
}
@objc func modalWindowClose(_ notitification: Notification){
//关闭session窗口状态
if let sessionCode = sessionCode {
NSApplication.shared.endModalSession(sessionCode)
self.sessionCode = nil
}
//关闭modal窗口状态
if let window = notitification.object as? NSWindow {
if self.modalWindow == window {
NSApplication.shared.stopModal()
}
//作用同 applicationShouldTerminateAfterLastWindowClosed
if window == self.window! {
NSApp.terminate(self)
}
}
}
}
在关闭最后一个window时整个应用退出,等价于上面代码中侦听器的关闭方式:
// 在关闭最后一个window时应用退出
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
在应用关闭后,点击Dock菜单再次打开应用,这个方法和上面的方法是一个互斥的关系,所以此方法返回true时applicationShouldTerminateAfterLastWindowClosed()一定要返回false:
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
self.window.makeKeyAndOrderFront(self)
return true
}
编程实现
通过程序来创建window窗口:
import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate {
var windowItem: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
}
func applicationWillTerminate(_ aNotification: Notification) {
NotificationCenter.default.removeObserver(self)
}
@IBAction func createWindow(_ sender: NSButton) {
let frame = CGRect(x:0, y:0, width: 400, height: 300)
//设置按钮样式
let style : NSWindow.StyleMask = [NSWindow.StyleMask.titled,NSWindow.StyleMask.closable,NSWindow.StyleMask.resizable]
/*
backing:缓存样式,完整写法是 NSWindow.BackingStoreType.buffered
defer:表示立即创建还是延迟创建
*/
windowItem = NSWindow(contentRect: frame, styleMask: style, backing: .buffered, defer: true)
windowItem.title = "window by code"
//显示窗口
windowItem.makeKeyAndOrderFront(self)
//点击窗口背景,支持鼠标拖动窗口
windowItem.isMovableByWindowBackground = true
//居中显示
windowItem.center();
//全屏
windowItem.toggleFullScreen(myWindow)
}
}
上面代码有一个问题,就是窗口只能显示一次,关闭后就不能再显示了,后面在NSWindowController控制器一节中会详细讲解:https://korgs.blog.csdn.net/article/details/142720096
窗口设置
窗口样式设置
可设置标题,图标和背景等
func setWindowTitleImage(){
self.window.representedURL = URL(string:"WindowTitle")
self.window.title = "WindowItem"
let image = NSImage(named: "WindowIcon")
//这行代码可以把图标放在源码下面,而不用放在Assets.xcassets中
//let image = NSImage(named: NSImage.Name(rawValue: "AppIcon.png"))
self.window.standardWindowButton(.documentIconButton)?.image = image
self.window.backgroundColor = NSColor.gray
}
窗口工具栏设置
以下代码会在标题栏上增加一个名为button的按钮
func addButtonToTitleBar(){
let titleView = self.window.standardWindowButton(.closeButton)?.superview
let button = NSButton()
let x = (self.window.contentView?.frame.size.width)! - 100
let frame = CGRect(x: x, y: 0, width: 80, height: 24)
button.frame = frame
button.title = "button"
button.bezelStyle = .rounded
titleView?.addSubview(button)
}

通过storyboard 管理UI
通常情况下很少有场景需要手工创建NSWindow对象,如果需要的话一般会通过NSWindowController对象来创建和管理。默认情况下NSWindow对象会交由AppDelegate对象代理管理(AppDelegate就是一个普通类,如有需要也可以换成其它类),不建议手功创建NSWindow的原因大体如下:
- 采用xib设计时,xib中会包含一个window对象,它AppDelegate代理;
- 采用storyboard设计时,会自动生成一个controller类,由它来管理Window对象;
- NSWindow的设置非常复杂;
通过上面设置创建Storyboard类型的项目后,项目的源码大体如下:
和XIB类型的项目区别是,Storyboard会自动创建一个ViewController.swift文件,原来在AppDelegate.swift中的NSWindow(通过NSView引用访问)对象会转移绑定在此Controller中。这样就比较方便管理,职责也比较清楚,即AppDelegate管理App, 而ViewController管理Window。
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//注意这里设置的方法名变了
override func viewDidAppear() {
self.view.window?.isRestorable = false
self.view.window?.title = "windowCustom"
self.view.window?.center()
}
override var representedObject: Any? {
didSet {
super.viewDidLoad()
}
}
}
这一小节涉及到NSWindow, NSView两个控件,先做到会用,后面会详细讲这两个控件。


6914

被折叠的 条评论
为什么被折叠?



