您好,欢迎来到WWDC。
嗨,大家好。我叫蒂娜。我在SwiftUI上工作。今天,我将讨论使用SwiftUI构建支持文档的应用程序。
什么文件
首先是什么文件。人们一直使用macOS iPadOS和iOS上的Finder和Files应用程序管理文件。他们可以使用标签,云文件提供程序和外部存储设备等功能根据需要组织项目。他们期望能够使用允许他们无缝地就地打开,查看和编辑这些文件的应用程序。这包括能够在原始文档上进行更改,所有支持打开文件的应用程序都可以访问该更改。这与将这些文件导入到一个由应用程序管理的数据库中的应用程序形成对比。添加导入的文档后,重新编辑原始文档的副本,并且原始文档不变。像Pixelmator这样的专业应用程序不仅允许用户执行就地打开文件管理,而且还提供强大的编辑功能,包括立即打开多个文件。Pixelmator,Keynote和Final Cut Pro等应用程序中的几乎所有功能都集中于允许用户执行此文档管理。我们通常将这些基于文档的应用程序称为该行为的指示。但是,打开文档是您的应用程序可以支持的功能,而无需像Xcode这样的完全基于文档的应用程序具有其他UI和其他功能,而其文档支持之外,而诸如Mail和Console之类的应用程序主要表现为非基于文档的外观,但支持打开其他文档,例如eml文件或崩溃报告。SwiftUI应用程序由应用程序,场景,和意见。通过使用另一个称为文档组的同步类型来完成对文档的支持。一个简单的文本编辑应用程序如下所示。
使用DocumentGroup
在您的应用程序中使用DocumentGroup时,您声明您的应用程序支持就地打开和管理此类文档,如SwiftUI谈话中的App Essentials中所示。我们的代码结构与应用程序的所有权层次结构匹配。
@main
struct TextEdit: App {
var body: some Scene {
DocumentGroup(newDocument: TextDocument()) { file in
TextEditor(text: file.$document.text)
}
}
}
在这种情况下,我们的应用程序包含一个文档组场景,该场景能够打开该文档内容的多个窗口,并且作为组成元素,它可以支持不同类型的多个文档组或一个窗口组和一个文档组。您可以将这些场景组合到您的应用程序中,SwiftUI会自动为您的应用程序提供这些场景的预期平台行为。其中包括特定于Mac和文档浏览器上文档应用程序状态跟踪和切换的标准UI元素,以及iOS上具有搜索字段和共享功能的导航栏。您’ 用最少的代码就能得到所有这些。现在,让我们使用DocumentGroup API构建一些东西。因此,我在iPad背景上使用SwiftUI制作了一个绘图应用程序的原型。我目前有一块画布,可以在其中添加不同颜色的形状并更改其形状。我认为它运作良好。因此,我想将其制作成可以保存和管理图形的应用程序。让我们看看如何做到这一点。让我们打开Xcode并创建一个基于文档的应用程序。我想让它同时在macOS和iOS上运行。因此,我将选择多平台模板。因此,我想将其制作成可以保存和管理图形的应用程序。让我们看看如何做到这一点。让我们打开Xcode并创建一个基于文档的应用程序。我想让它同时在macOS和iOS上运行。因此,我将选择多平台模板。因此,我想将其制作成可以保存和管理图形的应用程序。让我们看看如何做到这一点。让我们打开Xcode并创建一个基于文档的应用程序。我想让它同时在macOS和iOS上运行。因此,我将选择多平台模板。
我将其称为形状编辑。
这已经带有一些模板。让我们尝试构建并运行它。
现在,我们有了一个文本编辑应用程序,其中包含我们前面提到的所有文档支持功能。现在,在深入探讨之前,让我们看一下项目的设置。
我现在将重点关注macOS目标,但是我们将要使用的配置同时适用于iOS和macOS目标。让我们看一下info.plist。Xcode为基于文档的应用程序添加了文档类型部分。该标识符字段中的值是统一类型标识符。系统使用该标识符将文件与您的应用程序关联,因此,当系统上看到一个文档时,它便知道在您的应用程序中打开此类文档。在此,此标识符声明为导入类型。之所以导入它,是因为明文类型是由另一方声明的,因此我们导入这些类型的声明是为了告诉系统该应用知道该信息。对于我们的应用程序,我们将声明自己的类型。让我们回到这里。并创建一个。因为它 我们发明的一种类型,我们需要导出类型声明以告诉系统我们是该类型的权威所有者。为此,我们填写了导出的类型标识符部分。让我们对其进行描述。
我们将图形存储为二进制数据,以使其符合公共数据和公共内容。我还将分配一个扩展名。
这就是我们需要在这里进行更改的所有内容。让我们看一下主要功能。
文档类型ShapeEditDocument
为我们声明了一种文档类型ShapeEditDocument,它已经在创建新文档时将要使用的文档类型和基础文档都传递给了文档组,闭包参数的document属性是一个绑定,为我们提供了对基础对象的读写访问权限数据模型,即此文本编辑应用程序中的文本。文档组支持就地打开,该绑定使SwiftUI知道何时更新文本,以便它负责注册撤消和弄脏文档状态。让我们看一下内容视图,它是呈现文档的默认视图。它由一个文本编辑器组成。由于我们要创建一个形状绘制应用程序,因此我们将其替换为用户可以绘制的画布。但是我们稍后再讲。让我们看一下形状编辑文档。
它是一个符合FileDocument协议的值类型,该协议是磁盘上文档的表示形式。首先,我们将定义可读的内容类型。它是UT类型的数组,统一类型标识符。SwiftUI使用此标识用户要打开的文档的类型,并且仅允许此处定义的文档类型。示例文本在此处定义。
这应该与我们之前在目标info.plist的“文档类型”部分中输入的内容匹配。因此,让我们进行更改。注意两个声明之间的区别。在这里,我们使用exportAs:构造函数。之前我们使用导入到此构造函数的方法是,导入的类型是计算变量,因为它是导入的。这意味着它的价值会随着安装或卸载应用程序的时间而改变。在这里,我们使用导出的类型,以便可以将其声明为常量以获得更多信息。请参阅开发人员网站上UTType的文档。现在让我们更改一下。以我们自己的类型。接下来,让我们实现给定文件包装器和内容类型的文档的初始化。我们不需要此代码,因此我将其删除。
内容类型参数始终是应用程序支持的类型,并且有几种方法可以执行此操作。在这里,我们将使用我们的JSON解码器。而要使用JSON解码器,该类型需要符合可调用类型。我们还需要实现将文档写出为指定类型的文件。
我们也不需要这些。因此,我还将删除它们FileWrapper是序列化的目标。这是一个inout参数,我们可以创建一个新的FileWrapper或对其进行更新。再次,有几种方法可以做到这一点,我们将使用JSON编码器。
这就是我们要做的一切事情,以符合文件文档协议。注意,我们在这里使用值类型。这意味着您将获得使用结构的所有好处,包括写时复制行为,并且应用程序可以在用户仍在绘图时开始保存。现在,有了适当的文档支持,让我们将原型画布代码添加到项目中。我们有一个图形类型,用于定义形状的属性和用于显示形状的画布视图。让我们将文档中的数据类型从文本更改为图形类型。
我们还添加一些初始形状。 让我们用画布替换掉文本编辑器。我们都准备好了。让我们运行它。我们有一个支持文档的绘图应用程序。