Tải bản đầy đủ
Chapter 6. User Interfaces and iCloud

Chapter 6. User Interfaces and iCloud

Tải bản đầy đủ

making here is a little more complex than the UI from the last chapter, and we’ll need
certain parts of it in place before we can connect the code we’ll be writing to it.
1. Open Document.xib.
2. Resize the text view (using the handles, just like any GUI resize) we added in the
last chapter so that there’s more margin at the bottom of the window (see
Figure 6-1). This margin is where the collection view will go.

Figure 6-1. Resizing the text view to make room
3. Search for NSCollectionView in the Object library (see Figure 6-2). Drag in a
collection view. We’re going to use this to display any attachments for the open
note document.
NSCollectionView is provided by AppKit to display a grid of other views. Each
view it displays is managed by an NSCollectionViewItem.

To create the collection view, we also need to create the view for each cell. We
only need to create one of these views—the collection view will create and man‐
age multiple copies of them, one for each attachment in the documents.

128

|

Chapter 6: User Interfaces and iCloud

The closest equivalent for iOS is a UICollectionView, which
we’ll use later on, in “Collection Views” on page 202.

Figure 6-2. Finding the NSCollectionView
4. Resize the collection view to fill the margin beneath the text view, but leave some
space on the right hand side (see Figure 6-3).

Updating the UI

|

129

Figure 6-3. Adding the collection view
5. Select the collection view and open the Attributes Inspector. In the Layout
options, change it to Flow, which will create a nice, simple, linear layout for our
attachments.
6. Select both the text view and the collection view. Open the Editor menu, and
choose Resolve Auto Layout Issues, and choose Reset to Suggested Constraints.
7. Open Document.swift in the Assistant.
8. Hold down the Control key, and drag from the collection view into the Document
class. Create a new outlet connection called attachmentsList. You can now close
the Assistant if you need the screen space.
9. Hold down the Control key again, and drag from the collection view to the File’s
Owner in the outline. Choose “delegate” from the list that appears.
10. Hold down the Control key a third time, and drag from the collection view to the
File’s Owner. Choose “dataSource” from the list that appears.

130

|

Chapter 6: User Interfaces and iCloud

Because of the hierarchy of views in our interface, selecting
from the interface will often grab a parent object instead of the
view we want. It is generally easier to select the correct object
from the outline than from the views.

For just a few clicks and some dragging, we have done rather a lot. We added a collec‐
tion view to our interface and then we used the built-in tool to fix the constraints on
our interface. Next, we created an outlet for the collection so we can refer to it in our
code. Finally, we hooked up the delegate and dataSource properties of the collec‐
tion view to our Document.swift class. We’ve done all of this so we can refer to and
configure the collection view in our code.

Document-Filetype-Extension UI
Next, we need to design the view that will be used for each attachment in the collec‐
tion view. At the same time, we’ll create a new class that will act as the manager for
the cell’s view. We won’t be adding any code to this class right away, but it saves a little
time to do it now rather than to create the file later.
1. Create a new Cocoa class named AttachmentCell by going to File→New→File in
the menu or by pressing ⌘-N. Make it a subclass of NSCollectionViewItem, and
turn on “Also create XIB file for user interface,” as shown in Figure 6-4.

Document-Filetype-Extension UI

|

131

Figure 6-4. Adding the AttachmentCell
2. Open the newly created AttachmentCell.xib.
3. Go to the Object library, and search for Collection View Item (Figure 6-5). Drag
one into the outline view, at the left.

132

|

Chapter 6: User Interfaces and iCloud

Figure 6-5. Searching for a collection view item
4. We need to make the collection view use the AttachmentCell class, so select it
and go to the Identity Inspector. Change its class from NSCollectionViewItem to
AttachmentCell (Figure 6-6).

Document-Filetype-Extension UI

|

133

Figure 6-6. Changing the class for the collection view
We’ll now add an image view to represent the attachments, and a label to show
their file extension.
5. Search for NSImageView in the Objects library (Figure 6-7).

Figure 6-7. The NSImageView in the library
6. Drag in an image view, and place it in the center of the canvas. Resize it to give it
a bit of space around the edges (Figure 6-8).

134

| Chapter 6: User Interfaces and iCloud

Figure 6-8. Adding the image view
7. Next, we’ll add a label to show the file type.
8. Drag in a label from the Object library, and place it beneath the image view.
9. Select the new image view and label, and open the Editor menu. Choose Resolve
Auto Layout Issues→Reset to Suggested Constraints.
Next, we need to tell the collection view item about the image view and text field
that we just added.
10. Hold down the Control key, and drag from the attachment cell in the outline to
the image view. Select imageView in the menu that appears.
11. Repeat this process, but this time drag from the attachment cell to the label, and
select textField in the menu.
12. Repeat this process a third time, and Control-drag from the attachment cell onto
the view that contains the image view (not the image view itself). Select view in
the menu that appears.
Collection view items already have an outlet set up for an image
view and a text field, so you don’t need to create them yourself.

The interface for the collection view cells are now ready. It’s time to set up the Docu
ment class to be able to provide data to the collection view.

Document-Filetype-Extension UI

|

135

Getting an Icon for the Collection View Cells
As part of displaying attachments, we need some kind of picture to show them. We’ll
also display the file extension of each attachment in the label that you just created.
Because attachments are represented by NSFileWrapper objects, we need a way to get
a representative image from them. This means extending the NSFileWrapper class to
add a method that returns an image. We’ll also need a way to get the file’s extension so
that it can be displayed, too.
We’re talking about two types of “extension” here: one is the file’s
extension (e.g., the “rtf ” component of a filename Text.rtf) and the
other is in terms of a Swift extension, which we covered in “Exten‐
sions” on page 67. Don’t get confused! We often do.

1. Open Document.swift.
2. Add the following extension to the top of the file, outside of the Document class:
extension NSFileWrapper {
dynamic var fileExtension : String? {
return self.preferredFilename?
.componentsSeparatedByString(".").last
}
dynamic var thumbnailImage : NSImage {
if let fileExtension = self.fileExtension {
return NSWorkspace.sharedWorkspace()
.iconForFileType(fileExtension)
} else {
return NSWorkspace.sharedWorkspace().iconForFileType("")
}
}
func conformsToType(type: CFString) -> Bool {
// Get the extension of this file
guard let fileExtension = self.fileExtension else {
// If we can't get a file extension,
// assume that it doesn't conform
return false
}
// Get the file type of the attachment based on its extension
guard let fileType = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassFilenameExtension, fileExtension, nil)?

136

|

Chapter 6: User Interfaces and iCloud

.takeRetainedValue() else {
// If we can't figure out the file type
// from the extension, it also doesn't conform
return false
}
// Ask the system if this file type conforms to the provided type
return UTTypeConformsTo(fileType, type)
}
}

This extends NSFileWrapper to provide a means for getting a thumbnail—in this
case, the icon for a specific file extension—for each attachment. The extension is in
the Document.swift file because of the close relationship the file wrappers have with
the document—it makes sense to keep the related functionality together.
The fileExtension property takes the name of the NSFileWrapper and splits it up at
every . character. It then returns the last item in this list.
The thumbnailImage property takes the fileExtension and asks the NSWorkspace,
which represents the environment in which the app is running, to provide the image
used for files with this extension. If the extension is nil, a generic icon is used.
Finally, conformsToType takes the fileExtension and asks the operating system’s
type system to convert the file extension into an object representing that file type. If
this succeeds, that type object is used to check whether it conforms to the provided
type identifier.
If you are wondering what the takeRetainedValue() function is
doing, or why we have these weird-looking values like kUTTagClass
FilenameExtension, it’s because this code is using some libraries
that are written in the C programming language, not in Swift.
Unfortunately for us, C is not as nice as Swift, so we need to jump
through a few hoops and use some weird syntax to get the two lan‐
guages to play nicely with each other.

Adding Attachments
Finally, we need to add the button, which we’ll use to allow users to add new attach‐
ments. We’ll be adding code to this button shortly to actually make it work.
1. Open Document.xib.
2. Search for NSButton in the Object library (see Figure 6-9).

Document-Filetype-Extension UI

|

137

Figure 6-9. The NSButton in the library
3. Drag in a gradient button, and place it in the lower-right corner of the window
(Figure 6-10).
4. Resize it to 32 × 32.
5. Select the collection view, the text view, and the button. Open the Editor menu,
and choose Resolve Auto Layout Issues→Reset to Suggested Constraints.

138

|

Chapter 6: User Interfaces and iCloud