- The View Hierarchy
- Get a View to Draw Itself
- Drawing with NSBezierPath
- NSScrollView
- For the More Curious: Cells
- Challenge
Get a View to Draw Itself
In this section, you are going to create a very simple view. It will simply appear and paint itself green. It will look like Figure 12.4.
Figure 12.4 Completed Application
Create a new project of type Cocoa Application (Figure 12.5).
Figure 12.5 Choose Project Type
Name it ImageFun.
After the new project is created, open MainMenu.nib, and select NSView in the classes browser (Figure 12.6).
Figure 12.6 Select NSView
Press return to create a subclass, and name it StretchView (Figure 12.7).
Figure 12.7 Create the StretchView Class
Create the files for StretchView (Figure 12.8).
Figure 12.8 Create the Files for the StretchView Class
Save the files in the project directory.
Create an Instance of a View Subclass
Now create an instance of your class by dragging out a CustomView placeholder and dropping it on the window (Figure 12.9).
Figure 12.9 Drop a View on Your 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 12.10).
Figure 12.10 Set the Class of the View to StretchView
Notice that creating an instance of a view is different from creating an instance of a controller object like AppController. To create an instance of AppController in Chapter 7, you used the Instantiate menu item. When creating a view, it is important that you attach it to a window and give it a size and location in that window.
Size Info
Your StretchView object is a subview of the window's content view. An interesting question is: What happens to the view when the superview resizes? There is a page in the info panel that allows you to set that behavior. Open the size info panel, and set it as shown in Figure 12.11. This means that it will grow and shrink as necessary to keep the distance from its edges to the edges of its superview constant.
Figure 12.11 Make the View Resize with the Window
If you wanted the view to stay the same size, you could let the distance between the edges of the view and the edges of the superview grow and shrink. In this exercise, you do not want this behavior. But in a parallel universe where you did, the inspector would look like this (Figure 12.12).
Figure 12.12 Not This!
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. This method is called automatically, and you will never need to call it directly. Instead, if you know that a view needs redrawing, you will send the view the message setNeedsDisplay::
[myView setNeedsDisplay:YES];
This informs myView that it is "dirty." After the event has been handled, the view will be redrawn.
Before it is redrawn, the system locks focus upon it. Each view has its own graphics context. The graphics context includes things like 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.
You can use NSBezierPath to draw lines, circles, curves, and rectangles. You can use NSImage to composite images onto the view. In this example, you will fill the entire view with a green rectangle.
Open StretchView.m and add this method:
- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [[NSColor greenColor] set]; [NSBezierPath fillRect:bounds]; }
As shown in Figure 12.13, an NSRect is a struct with two members: origin, which is an NSPoint, and size, which is an NSSize.
Figure 12.13 NSRect, NSSize, and NSPoint
An NSSize is a struct with two members: width and height are both floats.
An NSPoint is a struct with two members: x and y are both floats.
Notice 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. This may be less than the entire view. If you are doing very time-consuming drawing, you should be careful to redraw only the dirty rectangle. This may speed up your application considerably.
Notice that setNeedsDisplay: will trigger the entire visible region of the view to be redrawn. If you wish to be more precise about which part of the view needs redrawing, you should use setNeedsDisplayInRect: instead:
NSRect dirtyRect; dirtyRect = NSMakeRect(0, 0, 50, 50); [myView setNeedsDisplayInRect:dirtyRect];
Build and run your app. Try resizing the window.