处理文件和文件类型
251
10-3
禁止文本视图滚动
19.
运行应用,现在文本在集合视图下面。
10.2
列出附件
现在,界面设计好了,下面要让
iOS
应用的
Document
类支持存储附件。
1.
打开
Document.swift
2.
添加下述代码实现
attachmentsDirectoryWrapper
属性,它返回一个
NSFileWrapper
对象,表示存储附件的文件夹。如果那个文件夹不存在,新建一个。
private var attachmentsDirectoryWrapper : NSFileWrapper? {
//
确保可以处理这个文档
guard let fileWrappers = self.documentFileWrapper.fileWrappers else {
NSLog("Attempting to access document's contents, but none found!")
return nil
}
//
尝试获取附件目录
var attachmentsDirectoryWrapper =
fileWrappers[NoteDocumentFileNames.AttachmentsDirectory.rawValue]
//
如果附件目录不存在
••••••
if attachmentsDirectoryWrapper == nil {
//
创建一个
252
10
attachmentsDirectoryWrapper =
NSFileWrapper(directoryWithFileWrappers: [:])
attachmentsDirectoryWrapper?.preferredFilename =
NoteDocumentFileNames.AttachmentsDirectory.rawValue
//
然后添加它
self.documentFileWrapper
.addFileWrapper(attachmentsDirectoryWrapper!)
//
我们修改了文件,做个记录
self.updateChangeCount(UIDocumentChangeKind.Done)
}
//
两种情况都要返回附件目录
return attachmentsDirectoryWrapper
}
attachmentsDirectoryWrapper
计算属性首先确保
Document
文件包装器中有一个
文件包装器数组可用。一般来说,肯定是有的,但是万一没有,就不能继续。
然后,尝试获取
Attachments
目录的文件包装器。如果不存在,首先创建一个,然后
把它添加到文档的文件包装器中。两种情况,最后都得到了
Attachments
目录,然后
将其返回。
3.
添加
attachedFiles
属性,返回一个
NSFileWrapper
数组
,每个元素表示一个附件
dynamic var attachedFiles : [NSFileWrapper]? {
//
获取附件目录中的内容
guard let attachmentsFileWrappers =
attachmentsDirectoryWrapper?.fileWrappers else {
NSLog("Can't access the attachments directory!")
return nil
}
// attachmentsFileWrappers
是一个字典,由文件名映射到
NSFileWrapper
对象。
//
我们只需要
NSFileWrapper
对象,所以返回一个数组
return Array(attachmentsFileWrappers.values)
}
为了获得所有附件,我们先确保有一个附件目录。然后,要做些转换
attachmentsDirectoryWrapper
fileWrappers
属性返回一个字典,由字符串映射到
NSFileWrapper
对象上。我们不关心文件名,只关心文件包装器,因此要让字典提供
values
值,然后让
Swift
把它转换成一个数组,再返回。
4.
添加
addAttachmentAtURL
方法,把指定文件复制到文档中,作为附件:
func addAttachmentAtURL(url:NSURL) throws -> NSFileWrapper {
处理文件和文件类型
253
//
确保有存放附件的地方
guard attachmentsDirectoryWrapper != nil else {
throw err(.CannotAccessAttachments)
}
//
使用指定文件创建一个附件,或者抛出错误
let newAttachment = try NSFileWrapper(URL: url,
options: NSFileWrapperReadingOptions.Immediate)
//
把文件添加到
Attachments
目录中
attachmentsDirectoryWrapper?.addFileWrapper(newAttachment)
//
标记自己需要保存
self.updateChangeCount(UIDocumentChangeKind.Done)
return newAttachment
}
这里为
Document
类添加附件的方式几乎与
Mac
应用中的同名方法一样(参见第
6
)。
我们首先确保有存放附件的地方,然后尝试为附件创建一个文件包装器,再把它添
加到
Attachments
目录中,最后记录文档被修改了。
10.2.1
判断附件的类型
为了列出附件,我们要以一种视觉方式呈现它们。也就是说,我们要显示某种缩略图。
首先,我们将添加默认的文件图像,如果应用不支持特定的文件类型,以这个图像做后备。
1.
打开
Assets.xcassets
2.
拖曳本书网站提供的资源中的
File.pdf
图像,把它添加到静态资源目录中。
接下来,我们将为文档提供判断附件类型的方式,还会添加一个方法,生成附件的
缩略图。为此,我们将为
NSFileWrapper
类添加几个方法,用于判断文件类型,然
后返回类型对应的
UIImage
对象。
3.
打开
Document.swift
4.
把下面一行代码添加到文件顶部,导入
MobileCoreServices
框架:
import MobileCoreServices
MobileCoreServices
框架用于访问统一类型标识符,即各种数据类型的统一标识符,
例如
PDF
、文本或
JPEG
。使用统一类型标识符,可以判断附件中的数据类型,而
不用从附件的名称中猜测。
254
10
5.
把下述代码添加到
Document.swift
文件中,为
NSFileWrapper
类添加一个扩展。我
们将在这个扩展中定义为
NSFileWrapper
类添加的额外方法:
extension NSFileWrapper {
}
6.
接下来,在这个扩展中添
fileExtension
属性和
conformsToType
方法,用于判断
文件的类型:
var fileExtension : String? {
return self.preferredFilename?
.componentsSeparatedByString(".").last
}
func conformsToType(type: CFString) -> Bool {
//
获取文件的扩展名
guard let fileExtension = fileExtension else {
//
如果无法获取文件扩展名,假定它不符合指定类型
return false
}
//
根据扩展名判断附件的类型
guard let fileType = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassFilenameExtension, fileExtension, nil)?
.takeRetainedValue() else {
//
如果从扩展名中无法判断文件类型,可能也不符合指定类型
return false
}
//
让系统判断文件类型是否符合指定类型
return UTTypeConformsTo(fileType, type)
}
fileExtension
属性直接在
.
处分拆
preferredFilename
然后返回数组中的最后一个元素。
这其实就是获取文件的扩展名。
String
类以前有个
pathExtension
属性,但是后来弃用了,可能是由于
NSURL
有个
同名方法。然而,因为
NSFileExtension
类无法处理
URL
,所以我们要自己动手处
理文件名。
conformsToType
方法的参数是存储在
CFString
中的
UTI
它让系统根据文件的扩展名(通
过前面的
fileExtension
属性获取)判断
UTI
如果那个
UTI
与参数传入的
UTI
一样,
返回
true
处理文件和文件类型
255
必须使用
takeRetainedValue
方法,因为
UTType
列方法是用
C
语言写的,不受
Swift
的内存管理系统控制。
takeRetainedValue
方法
在处理完毕之后通知
Swift
让它处理
UTTypeCreatePreferredIdentifierForTag
返回的值。
7.
最后,在扩展中添
thumbnailImage
方法,它使用
conformsToType
到的信息判
断该返回哪个图像:
func thumbnailImage() -> UIImage? {
if self.conformsToType(kUTTypeImage) {
//
如果是图像,作为
UIImage
对象返回
//
确保可以访问文件的内容
guard let attachmentContent = self.regularFileContents else {
return nil
}
//
尝试把文件的内容转换成文本
return UIImage(data: attachmentContent)
}
//
不知道是什么类型,返回
nil
return nil
}
我们会不断在
thumbnailImage
方法中添加代码,让它支持更多的附件类型。目
,它只检查文件包装器是不是图像文件;如果是,根据图像文件的内容返回一个
UIImage
对象
这里体现了
UTI
的强大。为了判断文件包装器是不是图像,我们无需手动检查文件
的扩展名是不是
.png
.jpg
.jpeg
等,而是直接询问系统。此外,如果
iOS
支持了
额外的图像格式,我们的代码能自动处理新格式。
10.2.2
显示附件单元格
现在,附件可以提供缩略图了,下面我们要让附件集合视图显示单元格。我们将为每个
附件显示一个单元格,此外还会显示一个“添加附件”单元格,轻点后会添加新的附件。
首先,为这个“添加附件”单元格添加所需的图像,然后把集合视图与文档视图控制器
连接起来。
1.
打开
Assets.xcassets
文件。

Get Swift学习手册 now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.