148
6
return attachmentsDirectoryWrapper
}
attachmentsDirectoryWrapper
属性表示文档包中的
Attachments
文件夹。访问这个
属性时,先做安全检查,获取文档中的文件包装器列表。然后检查这个列表中是否
已有
Attachments
目录;如果没有,则建一个,然后添加到文档中。最后,返回附件
的文件包装器。
2.
接下来,添加
addAttachmentAtURL
方法,它的作用是把新附件添加到文档中:
func addAttachmentAtURL(url:NSURL) throws {
guard attachmentsDirectoryWrapper != nil else {
throw err(.CannotAccessAttachments)
}
self.willChangeValueForKey("attachedFiles")
let newAttachment = try NSFileWrapper(URL: url,
options: NSFileWrapperReadingOptions.Immediate)
attachmentsDirectoryWrapper?.addFileWrapper(newAttachment)
self.updateChangeCount(.ChangeDone)
self.didChangeValueForKey("attachedFiles")
}
这个方法把一个附件添加到文档的
Attachments
目录中。首先,访问
attachments-
DirectoryWrapper
(调用刚才添加的那个属性)。然后告知系统,
attachedFiles
性的值会变。接下来,新建一个
NSFileWrapper
对象,包含指定的文件,再把文件添
加到
attachmentsDirectoryWrapper
中。最后,指明文档的内容确实变了。
3.
最后,添加
attachedFiles
属性,返回文档中现有的
NSFileWrappers
表:
dynamic var attachedFiles : [NSFileWrapper]? {
if let attachmentsFileWrappers =
self.attachmentsDirectoryWrapper?.fileWrappers {
let attachments = Array(attachmentsFileWrappers.values)
return attachments
} else {
return nil
}
}
用户界面和
iCloud
149
attachedFiles
属性获取一个字典,这是文件名到
Attachments
目录中文件包装器的
映射,然后返回文件包装器列表。这里的
dynamic
关键字,告诉监视这个变量变化
的其他对象,这个变量能被所在对象的其他部分修改。
我们计划在弹出窗口中显示
Add File
按钮,因此需要一个属性,存储那个弹出窗口。
弹出窗口由
NSPopover
支持,是显示在现有内容之上的用户界面。
OS X
iOS
都有弹出窗口。我们将在后面的第
11
章使用
iOS
的弹出窗口。
4.
Document
类中添加下述属性:
var popover : NSPopover?
5.
接下来,使用下述代码更新
addAttachment
方法:
@IBAction func addAttachment(sender: NSButton) {
if let viewController = AddAttachmentViewController(
nibName:"AddAttachmentViewController",bundle:NSBundle.mainBundle()
) {
self.popover = NSPopover()
self.popover?.behavior = .Transient
self.popover?.contentViewController = viewController
self.popover?.showRelativeToRect(sender.bounds,
ofView: sender, preferredEdge: NSRectEdge.MaxY)
}
}
注意,要把
sender
参数的类型改为
NSButton
。这一点很重要,因为
showRelativeToRect
方法的
ofView
参数要是
NSView
或其子类的
实例。
这个方法使用我们前面设计的
AddAttachmentViewController.xib
文件创建一个
AddAttachmentViewController
对象。接着,在弹出窗口中显示那个视图控制器,然
后在依附到按钮上时再次显示。
为了在用户选择一种附件类型时得到通知,我们要符合
AddAttachmentDelegate
150
6
议。我们将在一个扩展中编写这些代码,这样有助于把它与
Document
类的核心功能
区分开。
6.
Document.swift
文件的
Document
类外面添加下述扩展:
extension Document : AddAttachmentDelegate {
}
7.
在这个扩展中添加所需的
addFile
方法:
func addFile() {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.canChooseFiles = true
panel.beginWithCompletionHandler { (result) -> Void in
if result == NSModalResponseOK,
let resultURL = panel.URLs.first {
do {
//
得到一个
URL
,复制进来
try self.addAttachmentAtURL(resultURL)
//
刷新附件列表
self.attachmentsList?.reloadData()
} catch let error as NSError {
//
添加附件时出错了。
//
告知用户
//
尝试获取一个窗口,在里面显示一个附着对话框
if let window = self.windowForSheet {
//
在附着对话框中呈现错误
NSApp.presentError(error,
modalForWindow: window,
delegate: nil,
didPresentSelector: nil,
contextInfo: nil)
} else {
//
没有窗口,那就在普通对话框中呈现错误
NSApp.presentError(error)
}
}
}
}
}
用户界面和
iCloud
151
addFile
方法创建一个
NSOpenPanel
例,这个类由
Apple
提供,作用是让用户浏览
文件系统并选择文件。然后,为
NSOpenPanel
实例设定了几个选项:禁止用户一次
选择多个文件,禁止选择目录,只允许选择单个文件。
随后,
addFile
方法把这个面板呈现给用户,获得用户所选文件的
URL
。然后,把
URL
传给
addAttachmentAtURL
方法;如果出错了,则错误呈现给用户。
8.
addAttachment
方法,把文档设为
AddAttachmentViewController
委托对象:
@IBAction func addAttachment(sender: NSButton) {
if let viewController = AddAttachmentViewController(
nibName:"AddAttachmentViewController",bundle:NSBundle.mainBundle()
) {
> viewController.delegate = self
self.popover = NSPopover()
self.popover?.behavior = .Transient
self.popover?.contentViewController = viewController
self.popover?.showRelativeToRect(sender.bounds,
ofView: sender, preferredEdge: NSRectEdge.MaxY)
}
}
6.2.4
在集合视图中显示数据
至此,在集合视图中显示内容的工作做好了,但是还没有为集合视图提供数据的方法。
下面来添加!
为了给集合视图提供单元格,要把它的
dataSource
outlet
(前面创建好了)与符合
NSCollectionViewDataSource
协议的对象连接起来。这意味着,我们要让
Document
符合这个协议。我们将通过一个扩展实现。
1.
把下述代码添加到
Document.swift
文件中:
extension Document : NSCollectionViewDataSource {
}
为了符合
NSCollectionViewDataSource
协议,我们要实现两个方法。第一个方法告
诉系统有多少项目,第二个方法为集合视图提供一个
NSCollectionViewItem
对象,
用于显示。
152
6
2.
把下述方法添加到刚刚创建的扩展中:
func collectionView(collectionView: NSCollectionView,
numberOfItemsInSection section: Int) -> Int {
//
项目的数量等于拥有的附件数量。
//
如果由于某种原因,无法访问
attachedFiles
//
那么项目数量为零
return self.attachedFiles?.count ?? 0
}
??
是空合运算符
nil coalescing operator
)。如
??
左边的值为
nil
使用右边的值。
它是下述代码的简洁表述:
if let count = self.attachedFiles?.count {
return count
} else {
return 0
}
集合视图中的每个“区域
section
,即单元格组)都会调用这个方法。默认情
况下,一个集合视图中只有一个区域,因此我们忽略
section
参数,直接返回
attachedFiles
列表中的元素数量;如果无法访问
attachedFiles
列表,返回
0
接下来,我们要添加一个方法,为各个附件返回一个
NSCollectionViewItem
对象。
3.
在扩展中添加下述方法:
func collectionView(collectionView: NSCollectionView,
itemForRepresentedObjectAtIndexPath indexPath: NSIndexPath)
-> NSCollectionViewItem {
//
获取这个单元格应该呈现的附件
let attachment = self.attachedFiles![indexPath.item]
//
获取单元格自身
let item = collectionView
.makeItemWithIdentifier("AttachmentCell", forIndexPath:indexPath)
as! AttachmentCell
//
在单元格中显示图像和文件扩展名
item.imageView?.image = attachment.thumbnailImage
item.textField?.stringValue = attachment.fileExtension ?? ""
return item
}

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

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