Windows are instances of the class NSWindow. Each window has a collection of views, each of which is responsible for a rectangle of the window. The view draws inside that rectangle and handles mouse events that occur there. A view may also handle keyboard events. You have worked with several subclasses of NSView already: NSButton, NSTextField, NSTableView, and NSColorWell are all views. (Note that a window is not a subclass of NSView.)
The View Hierarchy
Views are arranged in a hierarchy (Figure 17.1). The window has a content view that completely fills its interior. The content view usually has several subviews, each of which may have subviews of its own. Every view knows its superview, its subviews, and the window it lives on.

Figure 17.1 Views Hierarchy
Here are the relevant methods from NSView:
- (NSView *)superview;
- (NSArray *)subviews;
- (NSWindow *)window;
Any view can have subviews, but most don't. The following five views commonly have subviews:
- The content view of a window.
- NSBox. The contents of a box are its subviews.
- NSScrollView. A view that appears in a scroll view is a subview of the scroll view. The scroll bars are also subviews of the scroll view.
- NSSplitView. Each view in a split view is a subview (Figure 17.2).

Figure 17.2 A Scroll View in a Split View
- NSTabView. As the user chooses different tabs, different subviews are swapped in and out (Figure 17.3).

Figure 17.3 A Tab View
Getting a View to Draw Itself
In this section, you will create a very simple view that will appear and paint itself green. It will look like Figure 17.4.

Figure 17.4 Completed Application
Create a new project of type Cocoa Application. Name the project ImageFun.
Using the File->New File menu item, create an Objective-C NSView subclass, and name it StretchView.
Create an Instance of a View Subclass
Open MainMenu.nib. Create an instance of your class by dragging out a CustomView placeholder from the Library (under Views & Cells -> Layout View) and dropping it on the window (Figure 17.5).

Figure 17.5 Drop a View on the Window
Resize the view to fill most of the window. Open the Info panel, and set the class of the view to be StretchView (Figure 17.6).

Figure 17.6 Set the Class of the View to StretchView
Size Inspector
Your StretchView object is a subview of the window's content view. This point raises an interesting question: What happens to the view when the superview resizes? A page in the Info panel allows you to specify that behavior. Open the Size Info panel, and set it as shown in Figure 17.7. Now it will grow and shrink as necessary to keep the distance from its edges to the edges of its superview constant.

Figure 17.7 Make the View Resize with the Window
If you wanted the view to stay the same height, you could let the distance between the bottom of the view and the bottom of the superview grow and shrink. You could also let the distance between the right edge of the view and the right edge of the window grow and shrink. In this exercise, you do not want this behavior. But if you did want the view to stick to the upper-left corner of the window, the Inspector would look like Figure 17.8.

Figure 17.8 Not This!
Figure 17.9 is a complete diagram of what the Size Inspector means.

Figure 17.9 What the Red Lines in the Size Inspector Mean
Save and close the nib file.
drawRect
When a view needs to draw itself, it is sent the message drawRect: with the rectangle that needs to be drawn or redrawn. The method is called automatically—you never need to call it directly. Instead, if you know that a view needs redrawing, you send the view the setNeedsDisplay: message:
[myView setNeedsDisplay:YES];
This message informs myView that it is "dirty." After the event has been handled, the view will be redrawn.
Before calling drawRect:, the system locks focus on the view. Each view has its own graphics context, which includes the view's coordinate system, its current color, its current font, and the clipping rectangle. When the focus is locked on a view, the view's graphics context is active. When the focus is unlocked, the graphics context is no longer active. Whenever you issue drawing commands, they will be executed in the current graphics context.
You can use NSBezierPath to draw lines, circles, curves, and rectangles. You can use NSImage to create composite images on the view. In this example, you will fill the entire view with a green rectangle.
Open StretchView.m and add the following code to the drawRect: method:
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
[[NSColor greenColor] set];
[NSBezierPath fillRect:bounds];
}
As shown in Figure 17.10, NSRect is a struct with two members: origin, which is an NSPoint, and size, which is an NSSize.

Figure 17.10 NSRect, NSSize, and NSPoint
NSSize is a struct with two members: width and height (both floats).
NSPoint is a struct with two members: x and y (both floats).
For performance reasons, structs are used in a few places instead of Objective-C classes. For completeness, here is the list of all the Cocoa structs that you are likely to use: NSSize, NSPoint, NSRect, NSRange, NSDecimal, and NSAffineTransformStruct. NSRange is used to define subranges. NSDecimal describes numbers with very specific precision and rounding behavior. NSAffineTransformStruct describes linear transformations of graphics.
Note that your view knows its location as an NSRect called bounds. In this method, you fetched the bounds rectangle, set the current color to green, and filled the entire bounds rectangle with the current color.
The NSRect that is passed as an argument to the view is the region that is "dirty" and needs redrawing. It may be less than the entire view. If you are doing very time-consuming drawing, redrawing only the dirty rectangle may speed up your application considerably.
Note that setNeedsDisplay: will trigger the entire visible region of the view to be redrawn. If you wanted to be more precise about which part of the view needs redrawing, you would use setNeedsDisplayInRect: instead:
NSRect dirtyRect;
dirtyRect = NSMakeRect(0, 0, 50, 50);
[myView setNeedsDisplayInRect:dirtyRect];
Build and run your app. Try resizing the window.
Drawing with NSBezierPath
If you want to draw lines, ovals, curves, or polygons, you can use NSBezierPath. In this chapter, you have already used the NSBezierPath's fillRect: class method to color your view. In this section, you will use NSBezierPath to draw lines connecting random points (Figure 17.11).

Figure 17.11 Completed Application
The first thing you will need is an instance variable to hold the instance of NSBezierPath. You will also create an instance method that returns a random point in the view. Open StretchView.h and make it look like this:
#import <Cocoa/Cocoa.h>
@interface StretchView : NSView
{
NSBezierPath *path;
}
- (NSPoint)randomPoint;
@end
In StretchView.m, you will override initWithFrame:. As the designated initializer for NSView, initWithFrame: will be called automatically when an instance of your view is created. In your version of initWithFrame:, you will create the path object and fill it with lines to random points. Make StretchView.m look like this:
#import "StretchView.h"
@implementation StretchView
- (id)initWithFrame:(NSRect)rect
{
if (![super initWithFrame:rect])
return nil;
// Seed the random number generator
srandom(time(NULL));
// Create a path object
path = [[NSBezierPath alloc] init];
[path setLineWidth:3.0];
NSPoint p = [self randomPoint];
[path moveToPoint:p];
int i;
for (i = 0; i < 15; i++) {
p = [self randomPoint];
[path lineToPoint:p];
}
[path closePath];
return self;
}
- (void)dealloc
{
[path release];
[super dealloc];
}
// randomPoint returns a random point inside the view
- (NSPoint)randomPoint
{
NSPoint result;
NSRect r = [self bounds];
result.x = r.origin.x + random() % (int)r.size.width;
result.y = r.origin.y + random() % (int)r.size.height;
return result;
}
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
// Fill the view with green
[[NSColor greenColor] set];
[NSBezierPath fillRect: bounds];
// Draw the path in white
[[NSColor whiteColor] set];
[path stroke];
}
@end
Build and run your app. Pretty, eh?
Okay, now try replacing [path stroke] with [path fill]. Build and run it.
NSScrollView
In the art world, a larger work is typically more expensive than a smaller one of equal quality. Your beautiful view is lovely, but it would be more valuable if it were larger. How can it be larger yet still fit inside that tiny window? You are going to put it in a scroll view (Figure 17.12).

Figure 17.12 Completed Application
A scroll view has three parts: the document view, the content view, and the scroll bars. In this example, your view will become the document view and will be displayed in the content view, which is an instance of NSClipView.
Although this change looks tricky, it is very simple to make. In fact, it requires no code at all. Open MainMenu.nib in Interface Builder. Select the view, and choose Embed Objects in Scroll View from the Layout menu (Figure 17.13).

Figure 17.13 Embed the StretchView in a Scroll View
As the window resizes, you want the scroll view to resize, but you do not want your document to resize. Open the Size Inspector, select Scroll View, and set the Size Inspector so that it resizes with the window (Figure 17.14).

Figure 17.14 Make Scroll View Resize with Window
Note the width and height of the view.
To select the document view, double-click inside the scroll view. You should see the title of the inspector change to Stretch View Size. Make the view about twice as wide and twice as tall as the scroll view. Set the Size Inspector so that the view will stick to the lower-left corner of its superview and not resize (Figure 17.15). Build the application and run it.

Figure 17.15 Make StretchView Larger and Nonresizing
Creating Views Programmatically
You will instantiate most of your views in Interface Builder. Every once in a while, you will need to create views programmatically. For example, assume that you have a pointer to a window and want to put a button on it. This code would create a button and put it on the window's content view:
NSView *superview = [window contentView];
NSRect frame = NSMakeRect(10, 10, 200, 100);
NSButton *button = [[NSButton alloc] initWithFrame:frame];
[button setTitle:@"Click me!"];
[superview addSubview:button];
[button release];
For the More Curious: Cells
NSControl inherits from NSView. With its graphics context, NSView is a relatively large and expensive object to create. When the NSButton class was created, the first thing someone did was to create a calculator with 10 rows and 10 columns of buttons. The performance was less than it could have been because of the 100 tiny views. Later, someone had the clever idea of moving the brains of the button into another object (not a view) and creating one big view (called an NSMatrix) that would act as the view for all 100 button brains. The class for the button brains was called NSButtonCell (Figure 17.16).

Figure 17.16 NSMatrix
In the end, NSButton became simply a view that had an NSButtonCell. The button cell does everything, and NSButton simply claims a space in the window (Figure 17.17).

Figure 17.17 NSButton and NSButtonCell
Similarly, NSSlider is a view with an NSSliderCell, and NSTextField is a view with an NSTextFieldCell. NSColorWell, by contrast, has no cell.
To create an instance of NSMatrix in Interface Builder, you drop a control with a cell onto the window, choose Embed Objects In -> Matrix, and then Option-drag as if resizing until the matrix has the correct number of rows and columns (Figure 17.18).

Figure 17.18 A Matrix of Buttons
An NSMatrix has a target and an action. A cell may also have a target and an action. If the cell is activated, the cell's target and action are used. If the target and action of the selected cell are not set, the matrix's target and action will be used.
When dealing with matrices, you will often ask which cell was activated. Cells can also be given a tag:
- (IBAction)myAction:(id)sender {
id theCell = [sender selectedCell];
int theTag = [theCell tag];
...
}
The cell's tag can be set in Interface Builder.
Cells are used in several other types of objects. The data in an NSTableView, for example, is drawn by cells.
For the More Curious: isFlipped
Both PDF and PostScript use the standard Cartesian coordinate system, whereby y increases as you move up the page. Quartz follows this model by default. The origin is usually at the lower-left corner of the view.
For some types of drawing, the math becomes easier if the upper-left corner is the origin and y increases as you move down the page. We say that such a view is flipped.
To flip a view, you override isFlipped in your view class to return YES:
- (BOOL)isFlipped
{
return YES;
}
While we are discussing the coordinate system, note that x- and y-coordinates are measured in points. A point is typically defined as 72.0 points = 1 inch. In reality, by default 1.0 point = 1 pixel on your screen. You can, however, change the size of a point by changing the coordinate system:
// Make everything in the view twice as large
NSSize newScale;
newScale.width = 2.0;
newScale.height = 2.0;
[myView scaleUnitSquareToSize:newScale];
[myView setNeedsDisplay:YES];
Challenge
NSBezierPath can also draw Bezier curves. Replace the straight lines with randomly curved ones. (Hint: Look in the documentation for NSBezierPath.)