本文作者:23 786
本文分类:Apple开发 浏览:2601
阅读时间:2541字, 约3-4分钟
作者提醒:请注意,本文写作时间为 2020 年,作者经验尚不成熟,会可能会含有非常多错误或主观的内容,请仔细甄别。
引言
在macOS下进行 Document-Based App 开发时,有时会遇到要同时打开多个文件,却不想把它们分成很多个窗口显示的情况。经过查找资料和实践出真知,我完成了我的应用这一部分的开发。
概念
NSWindowController
NSWindowController
有一个属性叫做 document
。
unowned(unsafe) var document: AnyObject? { get set } // Swift
// Objective-C
@property(assign) id document;
这个 document
属性代表了这个窗口控制器实例正在编辑的文档。
NSDocument
成员方法
func makeWindowControllers()
Creates the window controller objects that the document uses to display its content.
可以重载这个方法,来控制它所对应的窗口控制器。
NSDocumentController
控制了整个文件系统的打开、读取、新建。如果要自定义打开操作或者新建文件操作,则需要将它子类化。
正题
简单步骤:
- 创建自定义类型以控制多个文件
- 重载自定义类型里的方法,完成基本操作
- UI控件
创建子类以控制多个文件
我们在 NSwinndowController class
上进行操作。
由于一个 NSWindowController
只有一个 document
属性,显然无法满足我们多个文件的需求,于是应该新加入一个属性 documents
。为了避免重复,可以使用 NSOrderedSet
,但它是 Objective-C
硬搬过来的类,很难用,于是我这里用了普通 Swift Array
。
import Cocoa
class CDMainWindowController: NSWindowController {
var documents: [CDCodeDocument] = []
}
我们定义一个全局变量 GlobalMainWindowController
,表示那个文件窗口,方便操作。
由于需要自定义文件操作,所以子类化一个 NSDocument
类和一个 NSDocumentController
类。
import Cocoa
class CDCodeDocument: NSDocument {
var contentString = ""
// ......
}
class CDDocumentController: NSDocumentController {
}
重载自定义类型里的方法,完成基本操作
先在 Window Controller
里写几个基本操作方法。
// 打开或者新建一个文件,把这个文件加入到这个窗口控制器里
func addDocument(_ document: CDCodeDocument) {
// 重复文件
if documents.contains(document) {
return
}
self.documents.append(document)
self.document = document
// 其他UI更新
}
// 更改目前正在编辑的文件
func setCurrentDocument(index: Int) {
// 判断越界
guard index >= 0 && index < self.documents.count else{
return
}
self.document?.removeWindowController(self) // 别忘了!
let document = self.documents[index]
document.addWindowController(self)
self.document = document
// 其他UI更新
}
之后,更新其他类里面的方法。
先看 Document Controller
:
- 新建未命名文件时,要把这个新建的文件放进窗口里进行显示;
- 打开文件时,也要把这个打开的文件塞进窗口里显示。
可能还会有其他的情况吧……?先写这两个。
// 打开已有文件
override func openDocument(withContentsOf url: URL, display displayDocument: Bool, completionHandler: @escaping (NSDocument?, Bool, Error?) -> Void) {
super.openDocument(withContentsOf: url, display: displayDocument) {
(document, success, error) in
// 加入到窗口里面
GlobalMainWindowController.addDocument(document! as! CDCodeDocument)
completionHandler(document, success, error)
}
}
// 新建未命名文件
override func openUntitledDocumentAndDisplay(_ displayDocument: Bool) throws -> NSDocument {
let doc = try super.openUntitledDocumentAndDisplay(displayDocument)
if displayDocument {
// 将未命名的文件加入到窗口里面
GlobalMainWindowController.addDocument(doc as! CDCodeDocument)
}
return doc
}
再看 NSDocument
的子类的方法重写:
override func makeWindowControllers() {
self.addWindowController(GlobalMainWindowController)
}
在 makeWindowController()
中设置它自己的窗口控制器。
UI更新
视情况进行适当UI更新。比如我开发的这个代码编辑器中,就要更新编辑器中的代码,以及其他一些提示性控件。
The End
这个功能我研究了一两天,最终的效果还是不错的,但有很多小坑,有很多需要注意的点,有时候一个点疏忽了,用户体验就会差 INF
倍。
关于作者23 786
- 好难啊我有点看不懂我也是六年级啊不过我有亲身体会,这种题目要自己做 要不然还是会不懂的要不你去 问问老师父母或同学
- Email: yixuanzhuzhuzhu123@163.com
- 注册于: 2020-04-07 01:52:33
2020-11-9 20:15 审核通过
害。win下的mdi视图难写得很。
您写的好专业啊!
test