Mac开发---NSDocument

3,071 阅读5分钟

简述

最近在写的App是这种类型的,总结了总结,写了这篇博文,在掘金中没有客户端分类 我不知道该放到那个分类里 就放在iOS里吧

本文旨在介绍如何实现 基于文档的Mac App 若有描述不准确或错误的地方 还望多多指正,共同学习(๑•ᴗ•๑)

实现的效果也就是 在文档的打开方式中有自己的App可选,将文件拖拽至Dock栏中可以打开~

NSDocument

NSDocument类相当于一层封装~ ,类似于UIImage,无论是jpg格式还是png格式都以UIImage对象进行调用~

NSDocument则不仅可以对外持有 文档数据, 对内可以绑定 窗口控制器~.当打开文档时自动创建并展示与其绑定的窗口控制器~

Xcode提供的Document模板大概就是下面这个样子~

import Cocoa

class Document: NSDocument {
    var saveData:Data!

    //是否自动保存(出用户选择外的所有保存方式 都是非自动保存,像按钮..点击的方法中可以保存,其他的不能)
    override class var autosavesInPlace: Bool {
        return true
    }

    //用于为Document绑定WindowController,当打开新的Document时,对应的WindowController就会被打开~(不绑定也是可以的)
    override func makeWindowControllers() {
        let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
        let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController
        self.addWindowController(windowController)
    }

    //将当前文档保存时调用
    override func data(ofType typeName: String) throws -> Data {
        Swift.print("保存文件")
        return saveData
    }

    //当读取新的数据时调用
    override func read(from data: Data, ofType typeName: String) throws {
        saveData = data
        Swift.print("读取内容 来自\(self.fileURL)")
    }
    
    //自动保存时调用的方法~ 当前是否能判断是否
    override func autosave(withImplicitCancellability autosavingIsImplicitlyCancellable: Bool, completionHandler: @escaping (Error?) -> Void) {
        Swift.print("调用自动保存")

        //completionHandler(nil)
        completionHandler(NSError.init(domain: "错误提示", code: 0, userInfo: nil))
    }
}

NSDocumentController

顾名思义是负责对Document进行管理,是一个单例~ 方法也就主要是下面这些~

新建

NSDocumentController.shared.newDocument(nil)
//也可以直接init一个Document,然后加进去

打开

//用户通过面板打开一个新的Document 
NSDocumentController.shared.openDocument(nil)

//这里也可以使用NSDocument的init方法创建(使用的重载方法不同),但是这样创建的对象不再NSDocumentController的管理内~ 可以手动加入其中

保存


//保存所有文档
NSDocumentController.shared.saveAllDocuments(nil)

//也可以调用NSDocument的save方法

导出

这里应该调用的是 Document的 saveAs()/saveTo() 方法, 为了工整一些 我就放在这里了~

info.plist配置

NSDocument类支持的文档类型和导出的文档类型是在 info.plist中存储的~ 我们可以在Targets->info中进行设置~ 需要对Document TypeExport UTIs进行设置~~

这里就引入了一个概念UTI,它是Apple系统处理文档文件等数据的统一类型标识符,也可以把它理解为苹果爸爸对其他各类文档数据的别名,大家叫jpg,苹果爸爸叫它public.jpeg.除去UTI中约定好的部分,对于 我们自创的类型,则可以设置自创的别名~ 在这里我就使用已有类型了jpg 其他的UTI看这里Uniform Type Identifies

Document Types

Name: 文档的类型名称,可为空~

identifer: 文档标识,若是用自定义的类型,这里可以写自定义的类型.若是针对已有类型可以在UTI中找~

Class: 标识该类型对应在项目中的Document类,我使用的继承NSDocument的子类 MK_Document,所以这里填 $(PRODUCT_MODULE_NAME).MK_Document

Roler: 这里表示应用对文档的处理权限, Editor(读写),Viewer(只读).

Extensions: 文档后缀,不区别大小写~

Icon: 自定义类型在Finder中显示的样子(有时候可以,有时候不行,就和我用Movist看mkv视频,有时候可以带上Movist图标,有时候不行~ 要有懂这个图片设置的老铁 分享一蛤~)

至于其他的一些选项,我没用到

至于下面那个additional document type properties是设置额外的文档属性,在旧版Xcode中Document Types在这里的选项并没有这么多,那个时候需要用它设置额外的一些属性,我用的Xcode9上面提供的选项已经够用了~~

当我们设置多个Document Type且绑定至同一个Document上时,在新建的文档保存/导出时,会让我们选择格式~

Export UTLs

若是App只是使用已有的文档类型如jpg,则是在Document Types中设置就已经足够了,但是若要试用自定义类型的话,则需要用到

这里就简单展示了一下自定义类型,需要注意的是 上面除了Description外都是必填的~

identiferExtension这里是自定以的~怎么写都好~ 但是上下要一致

Conforms To这里我填写的是public.data,这里到底写什么,可以在UTIs中按需选择~

效果如下~~

FileWrapper

有些时候,我们的文档并非只是一个文档,还有类似于bundle这种类型的文档包,(在这里吐槽一句,其实MarkDown也应该做成文档包这种样式就好了~) 效果如下

配置FileWrapper和上面略有不同~

infor配置

主要就是将Conforms To改一下~ 然后在Document中实现对应的NSFileWrape方法 其他地方也就是换汤不换药

    //保存时调用
    override func fileWrapper(ofType typeName: String) throws -> FileWrapper {
        let  fileWrappers  = FileWrapper(directoryWithFileWrappers: [:])

        return fileWrappers
    }

    //读取时调用~ 包中的数据可以通过 filleWraper获取
    override func read(from fileWrapper: FileWrapper, ofType typeName: String) throws {
        for item in fileWrapper.fileWrappers! {
            Swift.print("获得\(item.key)")
        }
    }

总结

NSDocument是 对文档的封装,无论面对什么样的文档 都是NSDocument对象,和 NSData一样~ 这种设计方案是是值得我们借鉴的 尤其是在写SDK时~

若上文有不准确的地方 还望多多指正 共同学习 (๑•ᴗ•๑)