Giving the Simple Text Editor 'Legs'06/01/2001
In the last article, I discussed how to create an interface for a simple text editor, complete with spell-checking, font-styling, and much more. If you followed along, you were able to create a feature-rich application without any actual coding work. But now you need to add a little code to make it complete. Our text editor, being an application that works with documents, needs to behave as such. In Cocoa these are called document-based applications.
Primarily (at least for the context of this column) that means we should be able to save documents to disk and then open them at another time. Cocoa's document-based application sub-structure provides a programmatic paradigm and interface for access to printing, document opening and saving, undo services, and management of multiple simultaneous open documents. The class
NSDocument encapsulates and abstracts this behavior making it straightforward to create document-based applications. So it looks like it's time to talk about the
The reason for starting you out in Cocoa with a document-based application rather than the traditional "Hello World" type of thing was to make you realize how much power is available from the get-go. This application requires very little coding, yet allows you to do so much.
Additionally, since document-based applications are so ubiquitous, it makes sense to give you something you can use right away. An application that can store and open data in the form of a document is far more powerful and useful than a glorified area-of-a-circle calculator. I realize I'm throwing some fairly advanced stuff at you in this column, so don't worry if you don't understand it fully this time around. I will be getting back to all of this in more detail later on.
NSDocument and Cocoa document-based applications
If you've ever done anything at the keyboard, it's been working with documents. After all, this is what we do with computers. A computer isn't very useful if we can't save data and retrieve it at a later time. The applications that work with documents are, appropriately enough, called document-based applications. Because of the inherent utility and pervasive nature of these types of applications, it is worth our time to learn how Cocoa implements them.
In the most functional sense, a document is no more than a container for data -- a bucket for bytes if you will. In Cocoa, a document is a very intelligent bucket (a bucket with brains?) that knows how to do lots of useful things with its data. It can display data in a window, save it to a file, read saved data from a file, and even print it. The document and its interface define how users interact with the data in the bucket. This behavior is encapsulated in the Application Kit class
What are your observations to this point about programming in Cocoa?
Also in Programming With Cocoa:
It's important to understand the relation between
NSDocument, the application, the users, and data. Every document that is open in its application is represented by an instance of a subclass of
NSDocument. This means that you can have multiple documents open at the same time, and all of these documents are managed by the application.
In turn, each document object controls one or more windows that display the data of the document and allow users to interact with it. Understand that when we talk about documents, we can either be talking about the data -- all your spreadsheets and text files on your disk -- or we can be talking about the instance of an
NSDocument subclass that does the work behind the scenes. More often than not, I'll be talking about
I said earlier that we work with instances of a subclass of
NSDocument rather than an instance of
NSDocument is actually a special kind of class known as an abstract class. What this means is that
NSDocument declares many methods that encapsulate the essential behaviors of a document, but it does not attempt to define how those methods work. The implementation of these abstract methods is deferred to the subclass.
NSDocument doesn't know anything about the format or internal structure of a document's data, because every application has its own unique way of working with data. Rather, it is the responsibility of the subclass to provide the code that interprets the application's unique data.
NSDocument sets up and provides a set of services needed by all documents with its abstract methods, and you create a subclass that contains all the code needed to turn data on the disk into a picture of a mountain for your eyes, or music for your ears.
When you create a Cocoa document-based application in Project Builder, a subclass of
MyDocument is already set up and ready for you to fill in.
MyDocument contains a skeletal set of methods you must define for the application to work properly. The rest of this column will be concerned with implementing these methods to work with the text editor we started last time (if you haven't read the last article, it might by prudent to go do so now). Let's get to it!
Wiring up the interface
What we need to do now is make it possible to access and control the elements of your interface through source code. Interface Builder does this by letting you create outlets and actions in objects that we can work with in Project Builder.
Outlets are instance variables in a class that identify, or point to, another object, usually some interface object (buttons and text fields and such).
An action is a method that is invoked by some object in the interface; for example, when you push a button, it could be set up to invoke a particular action. We connect outlets and actions to the appropriate interface objects literally by wiring them together.
For our simple text editor, we need just one outlet that identifies our instance of
NSTextView. To set this up, click on the "Classes" tab in the
MyDocument.nib window. You will see a list of Cocoa classes available for you to subclass. Some of them are grayed out, and others are black. The black names indicate classes that you can add outlets or actions to; gray indicates a class that cannot be modified by the user without making a subclass.
Scroll through the list and find the class named
MyDocument, which is the subclass of
NSDocument given to us by Project Builder. To the right of the class name you see two icons. The one that looks like an electrical outlet represents outlets; the other one which is a target represents actions.
Creating and connecting an outlet
The number next to each one indicates the number of outlets and actions that have already been declared. If you click on either one of these, you will see the
MyDocument class expanded such that the outlets and actions are displayed. Here we see that there is already an outlet called window; this outlet is connected to the main document window in which we put the text view. Hit the Enter key while "outlets" is highlighted to create a new outlet, and name it "textView". Pressing tab or clicking in any of the gray area outside of MyDocument will return you to the previous view. You have now created an outlet.
Now we need to connect the
textView outlet to the actual the
NSTextView object in our interface. Return to the tab pane Instances. This pane shows all the objects that will be loaded into memory at runtime when the application loads your nib file. While holding the control key, click on the File's Owner icon and hold it while you drag the mouse over to the
NSTextView window. When you unclick, the Inspector window will open and a list of all possible connections will be displayed. You should see the
textView connection we just created. Highlight that and click the Make Connection button at the bottom of the window.
It should be noted that actions are wired in the direction of interface object to File's Owner, opposite of the direction for outlets. To help remember which direction you wire actions and which direction you wire outlets, think back to the way messaging works between objects that we talked about in the second column. When a button is pressed, it sends a message to
MyDocument telling it to perform a method. The message from the button is requesting an action to be performed. Outlets on the other hand, are the receivers of messages. If you need to do something to the text in a text view, you must send the
textView outlet a message to do so.
Now that we have made all the connections we need, let's go back to Project Builder and give our application some "legs."
Pages: 1, 2