Contact Us
HomeSite MapMini-Cart
Home Search Browse Used Books Customer Service  
      Contact Us
Find Books
Advanced Search
Blog Blog
Chapters
Categories
Coming Soon
On Sale
Slightly Worn
New Releases
Content
Chapters Articles
Chapters
Contest
Free Trade Magazines
Laugh
Tips
Join Our Mailing List

Your Email:

Subscribe
Update
Remove

Company
Contact Us
Customer Service
Policies and Procedures
Privacy and Security
International

 

Text Link Ads

  Chapters Home  

 
Sample Chapter - Custom Views
from the book: Cocoa Programming for Mac OS X, Third Edition  

>> This chapter has been viewed 7289 times.

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:

  1. The content view of a window.
  2. NSBox. The contents of a box are its subviews.
  3. 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.
  4. NSSplitView. Each view in a split view is a subview (Figure 17.2).


    Figure 17.2 A Scroll View in a Split View

  5. 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.)



© Copyright, Addison-Wesley. All rights reserved.

 

Print page     Email page


Search chapters 

 


AddThis Social Bookmark Button

Cocoa Programming for Mac OS X, Third Edition

Cocoa Programming for Mac OS X, Third Edition
 

Published by:
Addison-Wesley

Published:
05-08

Author:
Aaron Hillegass



Available from these sellers:
Amazon.com - 132 used & new from $0.01

Half.com - 82 used & new from $0.75



Search chapters

 



Print page

Email page

 

Macintosh Data Recovery

 

Award-winning Speech Recognition for Mac OS X

 

Bid on an Apple Macbook at Swoopo.com and save big!

 

Deal of the Day

 

Email this page to a Friend or Associate, and your name and email will automatically be entered in our contest to win a 1g Jump Drive and other great prizes!

 

more info
(opens in a new window)

 

 

 

Free Computer and Technical Magazines and White Papers!

 

 

 

 

 

 

 

 

 

 

---
Search I Book Index I Contact I Feedback
Copyright © 1997-2011 Computer Books Online
About Us I Publishers & Authors I Privacy Policy
All products and company names mentioned herein are the trademarks of their respective owners. No part of this website may be reproduced without the prior written permission of Computer Books Online. Prices and availability subject to change without notice.
This website may contain affiliate links and/or sponsered links.