Working With Bitmap Images; Document-Based Application Reduxby Michael Beam
One of the most useful and feature-rich aspects of Cocoa is its document-based application architecture. It's been awhile since we covered this ground - back when we first learned how to build Cocoa applications with the SimpleTextEditor application. However, that visit was a superficial glance at the surface that doesn't do justice to the capabilities provided by this part of Cocoa.
Today's column is the first in a series that takes a closer look at a number of previously-neglected aspects of Cocoa's document-based application feature set. The application we will build as a platform for exploring these concepts is an image viewer (and later an editor) application called ImageApp.
Subsequent columns will add image-exporting functionality, printing, undo, and a host of other features. Since we're constructing a graphics application, we'll also learn a great deal more about Cocoa graphics, in particular its image-handling classes. These classes include
NSImageRep and its subclasses, and more.
To kick things off, let's cover the mass of conceptual information that will provide a backdrop for our programming.
Document-based Application Refresher
In the document-based application architecture, three Application Kit classes are prime. They are
NSWindowController. Each of these classes has a specific role in the overall architecture of the document-based application, which follows the principles of the Model-View-Controller (MVC) pattern.
Recall that in the MVC pattern the application's functionality is split amongst several classes. The prototypical MVC pattern has three classes -- a class that models the data (the model), a class that displays the data (the view), and a class that is a mediator between the model and view (the controller). In past applications we learned how to implement this pattern by creating a controller class and using standard Cocoa classes as our model and view classes.
The document-based application architecture embraces the MVC pattern in its triad of classes and extends it somewhat by creating provisions for two controller classes. One controller class controls and manages the data model. This is the model-controller. The other controller serves the traditional role of controlling the user interface -- the view. This is the view-controller.
NSDocument is the model-controller, and
NSWindowController is the view-controller. Another way of looking at this is that
NSDocument owns and manages objects representing the document's data model, which in our case will simply be an instance of
NSImage. Part of the job of being the owner of the data objects is knowing how to load and save the persistent data, which is part of what
NSDocument is all about.
NSWindowController as the view-controller is the owner and controller over all objects that make up the user interface, and in particular, controls how the document's contents are displayed to the user. Completing the pattern are the non-document-related classes, such as
NSImage as the data model and
NSImageView acting as the primary view object.
NSDocumentController doesn't really fit into the MVC pattern, which is fine, as it has a different purpose in the application from managing and presenting data to the user. Quite simply,
NSDocumentController manages documents -- it knows how to create new documents (as an object, not the data of a document), how to open documents, and other application-level activities related to documents. This includes a knowledge of the types of files that the application can open (a viewer role), and that the application can modify and save (an editor role). Normally developers don't need to subclass
This is much different than the way we originally started out building document-based applications. Remember the simple SimpleTextEditor you built last year? That application was constructed around one class,
MyDocument, which contained all of the code to manage and control the user interface, as well as the data. Not only was it the model-controller, but it was also a view-controller. In light of the recent discussion of MVC, this was bad design.
Fortunately SimpleTextEditor remained a small application (at least as far as this column goes), and we didn't run into tricky design situations that resulted from poor foresight and planning. Clearly, if we were to modularize our code according to the principles of good object-oriented design, and the dictates of the MVC, we would want to separate the code that controls the interface from the code that manages the data model. This becomes exceedingly important when applications become more and more complex, as they inevitably do, so it's best to start out close to the way you wish to end up.
Before I move on, let me say a few more words about further associations between the three document-based application classes we have. The three classes
NSWindowController are related to one another in a one-to-many relationship.
That is, document-based applications have one
NSDocumentController object that manages one or more
NSDocument objects in turn have but one
NSDocumentController object. Similarly, instances of
NSDocument may own many instances of
NSWindowController, while any
NSWindowController object has but one
NSDocument master. Instances of
NSDocument keep a list of their window controllers which, when we write the code for using a custom window controller, we must add to.
If you're wondering what type of application would need multiple windows (every window controller has a window it owns), think of a CAD application, in which a single document may have three or four views of an object, each displayed in a separate window. This kind of document interface would be implemented by instantiating the required number of individual
NSWindowControllers and adding them to the document's list of window controllers.
So that's where we're at with document-based applications. My description was pretty bare on the details, so for a more complete analysis of what I talked about, check out the document at Apple's main Cocoa documentation page called Application Design for Scripting, Documents, and Undo; it is found under the legacy section.
Let's move on now and start building the application.