Document-Based App Programming Guide for iOS (一)

UIKit框架为管理多个文档的应用程序提供支持,每个文档包含存储在应用程序沙箱或iCloud中的文件中的唯一数据集。

这个支持的核心是在iOS 5.0中引入的UIDocument类。 基于文档的应用程序必须创建UIDocument的子类,它将文档数据加载到其内存数据结构中,并向UIDocument提供要写入文档文件的数据。 UIDocument负责管理与文档管理有关的许多细节。 除了与iCloud的集成外,UIDocument还会在后台读取和写入文档数据,以便您的应用程序的用户界面在这些操作中不会变得无响应。 它还可以自动定期保存文档数据,让用户免于明确保存。

概述

虽然基于文档的应用程序负责一系列行为,但是编写基于应用程序文档的应用程序通常不是一件困难的任务。

文档对象是模型控制器

在Model-View-Controller设计模式中,文档对象(即UIDocument的子类的实例)是模型控制器。文档对象管理与文档相关联的数据,特别是内部表示用户正在查看和编辑的模型对象。文档对象通常由向用户呈现文档的视图控制器来管理。

相关章节:设计基于文档的应用程序

设计应用程序时,考虑文档数据格式和其他问题

在编写代码之前,您应该考虑到基于文档的应用程序特定的设计方面。最重要的是,您的应用程序的文档数据的最佳格式是什么,以及如何使该格式在iOS和Mac OS X中为您的应用程序工作?什么是最合适的文件类型?

您还需要规划视图控制器(和视图)来管理打开文档,指示错误以及将所选文档移入和移出iCloud存储的任务。

相关章节:设计基于文档的应用程序,基于文档的应用程序预检

创建UIDocument的子类需要两个方法覆盖

文档对象的主要作用是将文档文件和内部表示文档数据的模型对象之间的数据“管理”。它给UIDocument类写入文档文件的数据,在读取文档文件之后,它使用UIDocument给出的数据来初始化其模型对象。为了履行此角色,您的UIDocument子类必须分别重写contentForType:error:method和loadFromContents:ofType:error:method。

相关章节:创建自定义文档对象

应用程序通过其生命周期管理文档

应用程序负责管理文档生命周期中的以下事件:

创建文件
打开和关闭文档
监视文档状态的更改并响应错误或版本冲突
将文档移动到iCloud存储(并将其从iCloud存储中删除)
删除文件
相关章节:管理文件的生命周期

应用程序在用户请求后存储iCloud中的文档文件

应用程序为用户提供将所有文档文件放在iCloud存储或本地沙箱中的所有文档文件的选项。要将文档文件移动到iCloud,它们将文档URL组织到应用程序的iCloud容器目录中,然后调用NSFileManager类的特定方法,传递文件URL。将文档文件从iCloud存储移动到应用程序沙箱遵循类似的过程。

相关章节:管理文件的生命周期

应用程序确保文档数据自动保存

UIDocument遵循无保留模式,并以特定的间隔自动保存文档的数据。用户通常不需要明确保存文档。但是,您的应用程序必须发挥其作用,才能使无节制模式正常工作,无论是通过执行撤消和重做,还是通过跟踪文档的更改。

相关章节:更改跟踪和撤消操作

应用程序解决不同文档版本之间的冲突

当文档存储在iCloud中时,可能会发生文档版本之间的冲突。发生冲突时,UIKit通知应用程序。应用程序必须尝试解决冲突本身或邀请用户选择他或她喜欢的版本。

相关章节:解决文件版本冲突

如何使用本文档

在开始为基于文档的应用程序编写任何代码之前,您应至少阅读前两章,设计基于文档的应用程序和基于文档的应用程序预检。这些章节将讨论设计和配置问题,并概述了精心设计的基于文档的应用程序所需的任务

先决条件

在阅读“基于文档的iOS应用程序编程指南”之前,您应该熟悉iOS应用程序编程指南中提供的信息。

也可以看看

以下文档以某种方式与基于文档的iOS应用程序编程指南相关:

统一类型标识符概述和相关参考讨论了统一类型标识符(UTI),它们是文档类型的主要标识符。
“文件元数据搜索编程指南”介绍了如何使用NSMetadataQuery类和相关类进行搜索。您使用元数据查询来查找存储在iCloud中的应用程序的文档。
iCloud设计指南提供了iCloud文档支持的介绍。

设计基于文档的应用程序

在iOS 5.0中引入的UIDocument类在基于文档的iOS应用程序中起主要作用。它是一个抽象的基类 - 这意味着你有一些有用的东西,你必须创建一个适合你的应用程序需求的子类。添加到子类中的特定于应用程序的代码增强了UIDocument可以实现的功能:与iCloud存储集成的移动环境中的文档的高效行为。

为什么要创建一个基于文档的应用程序?

当您有任何应用的想法并坐下来设计它时,您必须评估许多选项。您应该使用什么样的应用程序(主 - 细节,基于页面的实用程序,OpenGL游戏等)?应用程序是否会自己绘制图形,是否会对手势或触摸事件做出响应,并且会合并视听资产吗?如果应用程序使用核心数据,数据模型将是什么?是否使您的应用程序基于文档的决定可能似乎使这一系列选择变得复杂,但它真的归结为如何预期人们将使用您的应用程序。

当用户期望在可视容器中输入和编辑内容并以指定的名称存储该内容时,基于文档的应用程序是理想的,甚至是必要的。每个容器的信息 - 一个文件 - 是唯一的。您无疑熟悉桌面和移动系统中发现的几种基于文档的应用程序。举几个例子,有文字处理程序,电子表格和绘图程序。通过iCloud技术,您可以使您的应用程序文档更加引人入胜。例如,您的用户可以使用OS X桌面系统上的应用程序创建文档,然后再使用iOS版本的应用程序在iPad上编辑该文档,而无需进行任何同步或复制。这个功能使他们有更多的动机去购买你的应用程序。

当您采用基于文档的应用程序的UIDocument方法时,您的应用程序可以免费获取大量行为,或者以最少的编码工作。

与iCloud存储集成。 UIDocument对象协调从iCloud存储器读取和写入文档数据的所有内容。它通过采用NSFilePresenter协议和调用NSFileCoordinator和NSFileManager类的方法来实现。
后台写入和读写文件数据。如果您的应用程序同步读取和写入文档,它将变得暂时无响应。 UIDocument对象通过在背景调度队列上异步读取和写入文档数据来避免此问题。
无需保存的模型,基于文档的应用程序的用户很少需要明确保存文档; UIDocument对象按照用户正在使用的文档进行优化的间隔自动保存文档数据。您可以通过实施撤销管理或更改跟踪来实现基于文档的应用程序“无节制”(请参阅更改跟踪和撤消操作信息)。
安全保存,UIDocument对象安全地保存文档数据;因此,如果一些外部事件中断保存操作,文档数据将不会保持不一致的状态。该对象通过首先将最新版本的文档写入临时文件,然后用其替换当前文档文件来实现安全保存。
支持处理错误和版本冲突。当UIDocument对象检测到不同版本的文档之间的冲突时,它会通知应用程序。然后,应用程序可以尝试解决冲突本身,或者可以要求用户选择所需的文档版本。当保存操作不成功时,UIDocument对象还会通知应用程序。管理文档的生命周期描述了如何观察这些通知;解决文档版本冲突讨论了处理文档版本冲突的策略

iOS中的文档

虽然文档的广泛定义是“信息容器”,但可以通过多种方式查看该容器。对于用户,文档是他或她以唯一的名称创建,编辑和保存的文本,图像,形状和其他形式的信息。文档还可以参考文档文件:文档数据的持久磁盘表示。一个文档可以表示一个UIDocument对象,它表示和管理同一数据的内存中的表示。

文档对象在将文档数据在其在磁盘上的表示与其在存储器中的表示之间的转换中也起关键作用。它与UIDocument类合作,将文档数据写入文件并读取该数据。对于写入,文档通常提供可以写入文档文件的数据的快照;为了阅读,它接收数据并用它初始化文档的模型对象。在某种意义上,文档是存储在文件中的数据与该数据的内部表示之间的通道。图1-1说明了这些关系。

图1-1由文档对象管理的文档文件,文档对象和模型对象

Document-Based App Programming Guide for iOS (一)_第1张图片
document_model_2_2x.png

iCloud存储中的文档

在iCloud中,文件驻留在与应用程序关联的容器目录中。该目录具有内部结构,其中最重要的是一个Documents子目录。在iCloud方案中,写入Documents子目录的文件和文件包将被视为文档文件 - 即使它们不是基于文档的应用程序。写入容器目录中其他位置的文件被视为数据文件。

文档对象是文件呈现器,因为UIDocument类采用NSFilePresenter协议。文件演示者与NSFileCoordinator对象一起使用,以协调对应用程序对象之间以及应用程序和其他进程之间的文件或目录的访问。文件演示者涉及其他客户端访问相同的文件或目录。

当用户要求将文档文件存储在iCloud存储中时,基于文档的应用程序应将这些文件(包括文件包)保存在iCloud容器目录的Documents子目录中。有关详细信息,请参阅管理文档的生命周期。有关iCloud移动容器的更多信息,请参阅iCloud设计指南。

UIDocument对象的属性

文档对象具有多个定义属性,其中大部分与其作为文档数据管理器的角色有关。这些属性由UIDocument类声明。

档案网址,文档必须具有可以存储的位置,无论该位置在本地文件系统还是在iCloud存储中。 fileURL属性标识此位置。创建UIDocument对象时,必须指定文件URL作为UIDocument的initWithFileURL:initializer方法的参数。
文件名称。 UIDocument对象从文件URL的文件名组件获取默认文档名称,并将其存储在localizedName属性中。您可以覆盖此属性的getter方法来提供自定义的本地化文档名称。
注意:文档的显示名称不必与文档的文件名对应。此外,不需要用户指定文档名称。有关此主题的更多信息,请参阅创建新文档。
文件类型。文件类型是从文件URL的扩展名导出并分配给fileType属性的统一类型标识符(UTI)。有关详细信息,请参阅iOS如何识别应用程序的文档。
修改日期。文档文件上次修改的日期。该值存储在fileModificationDate属性中。它可以用于(除其他外)解决文档版本冲突。
文件状态文档在其运行期间处于几种可能状态之一。这些状态可以指示,例如,保存文档或存在冲突的文档版本时出错。 UIDocument将当前文档状态存储在documentState属性中。有关观察文档状态更改的信息,请参阅监控文档状态更改和处理错误。

基于文档的应用程序的设计注意事项

在为基于文档的应用程序编写一行代码之前,请考虑几个设计问题。

定义对象关系

与所有应用程序一样,您应该为基于模型 - 视图 - 控制器设计模式(MVC)的基于文档的应用程序的对象设计整体设计。虽然建议使用以下MVC设计文件,您可以自由地提出自己的对象关系设计。

在MVC术语中,文档是模型控制器;它“拥有”并管理表示文档内容的模型对象。文档对象本身由视图控制器拥有和管理。根据定义,视图控制器还管理呈现由文档对象管理的内容的视图。因此,它是这种关系网络中的中介控制器,从文档对象获取需要呈现的数据,并将用户输入或更改的数据传递给文档对象。图1-2描述了这些对象关系。

图1-2视图控制器管理文档对象和呈现文档数据的视图

Document-Based App Programming Guide for iOS (一)_第2张图片
document_model_2_2x.png

正如视图控制器嵌入其视图一样,视图控制器可以将文档对象嵌入为声明的属性。当应用程序实例化视图控制器时,它将使用文档对象或文档的文件URL(视图控制器本身可以创建文档对象)来初始化它。

当然,基于文档的应用程序将具有其他视图控制器(带有其视图)和可能的其他模型对象。要了解可能需要其他视图控制器的信息,请参阅设计用户界面。

设计用户界面

UIKit和开发人员工具都不会为基于文档的应用程序的用户界面提供任何支持。例如,没有UIDocumentView类或UIDocumentViewController类,没有用于选择文档的标准接口。不要后悔缺席,而是为了为您的基于文档的应用程序创建一个使其脱颖而出的用户界面。

尽管如此,所有基于文档的应用程序都应使用户能够执行需要用户界面元素的某些功能;这些包括以下内容:

  • 查看和编辑文档
  • 创建一个新的文档
  • 从应用程序所拥有的文档列表中选择文档
  • 打开,关闭和删除所选文档
  • 将选定的文档放在iCloud存储中(并从iCloud存储中删除选定的文档)
  • 指示错误条件,包括文档版本冲突
  • 撤消和重做更改(推荐但不需要)
    设计应用程序时,请确保包含执行这些操作所必需的视图控制器,视图和控件。

选择文档数据的类型,格式和策略

在为基于文档的应用程序设计数据模型时,请问和回答以下问题至关重要:

我的文档类型是什么?

文档必须具有文档类型,表示文档特征的信息集合,并将其与应用程序相关联。文档类型具有与一个或多个文件扩展名配对的名称,图标(可选),处理程序排名和统一类型标识符(UTI)。对于在iOS和OS X中编辑相同文档的应用程序,文档类型信息应该是一致的。

创建和配置项目说明了如何在基于文档的iOS应用程序中指定文档类型。有关UTI和统一类型标识符的描述,请参阅统一类型标识符概述常见的统一类型标识符的说明。

我应该如何表示写入文件的文档数据?

你基本上有三个选择:

Core Data。核心数据是对象图管理和持久性的技术和框架。它可以使用内置的SQLite数据库作为数据库系统。 UIManagedDocument类是UIDocument的子类,用于使用Core Data的基于文档的应用程序。
支持的对象格式,UIDocument支持NSData和NSFileWrapper对象作为文档数据表示的本机类型。 NSData适用于平面文件,NSFileWrapper适用于文件包,它们是iOS视为单个文件的目录。如果给UIDocument这些对象之一,它会将其保存到文件系统中,而无需进一步参与。
自定义对象格式,如果要将写入文件的文档数据为NSData或NSFileWrapper以外的类型,则可以在特定的UIDocument方法的替换中自己将其写入文件。请记住,您的代码必须重复UIDocument为您做的,因此您必须处理更大的复杂性和更大的错误可能性。有关更多信息,请参阅UIDocument类参考。

为什么要将文档数据存储在数据库而不是文件中?

如果由文档对象管理的数据集主要是一个较大的对象图,但是您的应用程序随时只使用该图形的一个子部分,则UIManagedDocument和Core Data是一个很好的选择。核心数据为基于文档的应用程序带来许多好处:

  • 增量读写文件资料
  • 支持撤消和重做操作
  • 自动支持解决文档版本冲突
  • 跨平台应用程序的数据兼容性
    核心数据的“缺点”是它是一项复杂的技术;熟悉其概念,课程和技术需要时间。要了解更多信息,请阅读“核心数据编程指南”和“UIManagedDocument类参考”。

支持的对象格式(NSData或NSFileWrapper)最适合我的应用程序?

是否对文档数据使用NSData或NSFileWrapper对象取决于数据的复杂程度以及文档的模型对象图形的部分内容是否可以单独写入文件。例如,如果您的文档数据是纯文本,而没有其他数据,则NSData是适合的容器。但是,如果数据具有多个组件(例如文本和图像),请使用NSFileWrapper方法为数据创建文件包。

  • 文件包装器对象和它们所代表的文件包比二进制数据对象提供了一些优势。

  • 文件包装器支持增量保存。与单个二进制数据对象相反,如果文件包装器包含文档数据(例如文本和图像),文本更改,则只有包含文本的文件必须写入磁盘。此功能可以带来更好的性能。
    文件包装(和文件包)使得版本文档更容易。例如,文件包可以包含一个属性列表文件,其中包含有关文档的版本信息和其他元数据。
    将文档数据存储在文件包中讨论(并说明)如何使用NSFileWrapper对象来表示可以作为文件包编写的表单中的文档数据。

我可以归档我的模型对象图并将该NSData对象写入文档文件吗?

是的,这是可能的,但前面提到的一些注意事项适用。如果您的文档的模型对象图形的任何部分可以写入单独的文件,请不要归档该部分。相反,将NSData存档和分区项目存储为文件包的单独组件。

如果我的文档文件非常大,该怎么办?

如果存储在文档文件中的数据可能很大,您可能必须逐步写入和读取数据,以确保良好的用户体验。你可能会采取几种方法:

  • 使用UIManagedDocument。记住,核心数据可以免费增加阅读和写作。
  • 将文档数据的可分离组件存储在文件包中。
  • 覆盖UIDocument的较低级别的方法,并自己进行数据的读写。 (有关详细信息,请参阅UIDocument类参考。)应始终使用适用于数据类型的增量读写API,例如,AV Foundation框架具有用于增量阅读大型多媒体文件的方法。

如果我希望我的文档在两个平台上可以编辑,我该怎么考虑?

如果您的用户可以在iOS移动设备(iPhone,iPad和iPad touch)和OS X桌面系统上创建和编辑文档,您的应用程序将具有竞争优势。但是为了可以这样做,两个平台上的文档数据的格式应该是兼容的。文档数据兼容性的一些重要注意事项是:

一些技术在一个平台上可用,但不是另一个。例如,如果您在OS X中使用RTF作为文档格式,则该格式在iOS中将无法运行,因为其文本系统不支持富文本。
每个平台中的相应类别不兼容。这对于颜色(UIColor和NSColor),图像(UIImage和NSImage)以及Bezier路径(UIBezierPath和NSBezierPath)尤其如此。例如,NSColor对象是根据颜色空间(NSColorSpace)定义的,但在UIKit中没有颜色空间类。
如果您使用其中一个类定义了一个文档属性,则必须设计一种方法(在准备文档数据以进行写入时)将该属性转换为可以在另一个平台上精确重构的表单。这样做的一个方法是将“下拉”到两个平台共享的较低级别的框架。例如,UIColor定义了一个CIColor属性,它保存一个表示颜色的Core Image对象;在OS X端,您的应用程序可以使用colorWithCIColor:class方法从CIColor对象创建一个NSColor对象。

每个平台的默认坐标系是不同的,这种差异可能会影响内容的绘制方式。 (有关此主题的讨论,请参阅iOS绘图和打印指南中的iOS中的坐标系和绘图)。
如果您部分或全部归档文档的模型对象图形,则在对模型对象进行编码和解码时,您可能必须使用NSCoder方法执行平台敏感转换。

基于文档的应用程序预检

对于大多数开发人员来说,制作基于文档的应用程序比基于文档的应用程序不需要更多的工作。基本的区别在于您必须创建UIDocument的自定义子类,然后通过其运行时生命周期来管理文档,包括与iCloud存储的集成。本章概述了大多数应用程序执行的特定于文档的任务,并描述了在iOS中创建和配置基于文档的应用程序项目的一般步骤。

基于文档的应用程序您必须做的事情

要创建基于文档的应用程序,您应该完成以下任务:

  • 创建UIDocument的自定义子类,为UIKit框架提供文档数据的快照,并从文档文件的内容中初始化文档的模型对象。
    创建自定义文档对象描述所需的方法覆盖,如果将文档数据存储为文件包,说明如何使用NSFileWrapper对象进行文档数据。

  • 允许用户创建新文档,并选择并打开现有文档。您还应执行关闭文档和删除所选文档的补充任务。
    请注意,创建或打开文档的后续任务是在视图中显示文档的内容。

创建新文档,打开和关闭文档以及删除文档描述这些任务的要求和步骤。这些讨论还包括管理文档数据的显示的示例。

  • 实施撤销管理或更改跟踪功能,实现自动保存文档数据(无保存模型)。
    有关详细信息,请参阅更改跟踪和撤消操作。

  • 将文档(通常由用户选择的文档)放入iCloud存储。当用户请求时,也可以从iCloud存储中删除文档。
    从iCloud Storage移动文件讨论这些过程。

  • 观察文档状态更改的通知,如果发生错误,请适当回复。
    监控文档状态更改和处理错误描述了执行此操作的一般步骤。

  • 如果发生不同版本的文档之间的冲突,请通知用户并提供解决冲突的方法。
    解决文档版本冲突描述了如何通知用户版本冲突,并讨论解决这些冲突的策略。

您还可以为基于文档的应用程序添加额外的功能,例如打印文档,拼写检查或将其发送给其他人的功能。

如果您的应用程序具有高级要求(例如,增量读取和写入大型文档文件或处理除支持的文档数据格式之外的文档数据格式),请参阅UIDocument类参考。所有这些高级任务都涉及覆盖UIDocument方法。

创建和配置项目

为基于文档的应用程序创建Xcode项目时,请选择合适的模板。 (请注意,没有专门针对基于文档的应用程序的模板。)通常,您希望应用程序的第一个视图是用户可以选择现有文档并创建新应用程序的视图。因为Master-Detail Xcode模板适用于此目的,所以在本文档的代码示例中使用。此模板为您提供了iPhone的初始表视图和iPad的拆分视图。您的项目应使用故事板,因此请务必选择此选项。

注意:如果您使用Core Data来管理文档数据,请务必在“新建项目”助手中选择“使用核心数据”选项。
根据您的应用程序的设计(请参阅设计基于文档的应用程序),创建应用程序所需的视图控制器子类。所有你需要的是最低限度声明的头文件和源文件在这一点上。然后,在项目故事板中创建应用程序的用户界面(如果您的应用程序是通用应用程序,则创建故事板);将自定义视图控制器与故事板中的视图控制器占位符相关联。

接下来,您需要通过在Xcode目标设置中指定应用程序知道的文档的类型或类型来配置文档项目。

iOS如何识别您的应用程序的文档

iOS中文档对象最重要的属性是其文件URL(fileURL)。文件网址很重要,除其他原因外,因为它告诉iOS哪些应用程序了解文档的格式。文件URL以扩展名(例如html)结尾,此扩展名与统一类型标识符(例如,public.html)匹配。统一类型标识符(UTI)是文档类型的主体标识符。使用扩展名,UIDocument查找文档类型UTI(如图2-1所示),并将其分配给fileType属性。与OSX中的基于文档的应用程序不同,iOS中的应用程序不需要将UIDocument子类与文档类型相关联。

图2-1 iOS从其文件URL的扩展名查找文档UTI

Document-Based App Programming Guide for iOS (一)_第3张图片
lookup_uti_from_extension_2x.png

文件类型UTI可以由系统定义;请参阅系统声明的统一类型标识符中的统一类型标识符参考这些常见标识符的列表。基于文档的应用程序还可以为其文档定义自己的专有UTI(并且通常是)。如果它声明一个自定义UTI,它还必须导出该UTI以使操作系统了解它。

声明文档类型
要在Xcode中声明文档类型,首先单击目标信息设置中的添加按钮,然后从弹出菜单中选择添加文档类型。单击“无标题”旁边的三角形以公开属性字段,并添加表2-1中的属性。

表2-1定义文档类型的属性(CFBundleDocumentTypes)

Xcode字段 价值和评论
LSItemContentTypes 类型 一系列UTI字符串。通常每个文档类型指定一个。
CFBundleTypeName 名称 文档类型的可选名称。
CFBundleTypeIconFiles 图标 应用程序包中图标图像文件的路径数组。
CFBundleTypeExtensions 在“附加文档类型属性”表中。 与文件UTI配对的文件扩展名数组。
LSHandlerRank 在“附加文档类型属性”表中。 所有者,替代,无。 (通常是所有者)。
LSTypeIsPackage 在“附加文档类型属性”表中。 如果文档数据存储在文件包中,请将此属性设置为YES。否则省略。

有关这些键的更多信息,请参阅信息属性列表键参考中的CFBundleDocumentTypes。

完成输入文档类型的属性后,Xcode的“文档类型”区域应类似于图2-2中的示例。

图2-2 Xcode中文档类型的规范

Document-Based App Programming Guide for iOS (一)_第4张图片
document_model_2_2x.png

应用程序可以有多种类型的文档 - 例如,文字处理应用程序可能具有常规(空白)文档的类型,另一种类型可以是预格式化的文档。对于每种类型,您需要通过上面给出的程序。

导出文档UTI

如果您为文档定义了自定义UTI,则还必须导出它。要导出Xcode中的文档类型,首先单击目标信息设置中的添加按钮,然后从弹出菜单中选择添加导出的UTI。单击Untitled旁边的三角形以公开属性字段,并添加表2-2中的属性。

注意:声明和导出UTI的过程在“统一类型标识符概述”中声明新的统一类型标识符中进行了说明。
表2-2导出文档的属性UTI(UTExportedTypeDeclarations)

Xcode字段 价值和评论
UITypeIdentifier 识别码 自定义文档UTI,一个字符串。
UTTypeConformsTo 符合 UTI符合UTI的自定义文档。如果数据表示是文件包,请指定com.apple.package。
UTTypeDescription 描述 导出类型的说明(可选)。
UTTypeTagSpecification 在“其他导出的UTI属性”表中。 创建一个名为public.filename-extension的数组。然后作为项目添加文档文件的所有扩展名。

有关这些键的更多信息,请参阅信息属性列表键参考中的CFBundleDocumentTypes。

输入文档类型的属性后,Xcode的Exported UTIs区域应与图2-3中的示例类似。

图2-3在Xcode中导出自定义文档UTI

Document-Based App Programming Guide for iOS (一)_第5张图片
export_doc_UTI_2x.png

你可能感兴趣的:(Document-Based App Programming Guide for iOS (一))