A Closer Look at the NSView Class

NSView is one of Cocoa’s most complicated classes. If you understand how it works, you can control the display of information on the computer’s screen and have it updated quickly and efficiently.

NSView Coordinate Systems

Each Cocoa NSView has its own coordinate system that can be rotated, scaled, or otherwise transformed from the coordinate system of its superview.

Each NSView also has the following two methods that describe its position in its window:

- (NSRect)frame

Returns the NSView’s frame in the coordinate system of its superview

- (NSRect)bounds

Returns the NSView’s frame (i.e., bounds) in its own coordinate system

When you change an NSView’s coordinate system, its bounds instance variable is automatically updated to reflect the change, while its frame instance variable remains the same. The NSView class provides the following methods for inspecting and changing an NSView’s coordinate system:

- (float)boundsRotation

Returns a floating-point number for the angle, in degrees, between an NSView’s coordinate system and the coordinate system of its superview.

- (float)frameRotation

Returns the angle of the NSView’s frame relative to its superview’s coordinate system. A value of 0 means that the NSView has not been rotated (but its coordinate system may have been).

- (BOOL)isRotatedFromBase

Returns TRUE if an NSView or any of its ancestors have been rotated from the window coordinate system.

- (BOOL)isRotatedOrScaledFromBase

Returns TRUE if an NSView or any of its ancestors have been rotated or scaled from the window coordinate system.

- (void)rotateByAngle:(NSCoord)angle

Rotates an NSView’s coordinate system around the NSView’s origin (0,0). This method rotates the contents of the view but not the view itself.

- (void)scaleUnitSquareToSize:(NSSize)newUnitSize

Scales an NSView’s coordinate system. For example, a newUnitSize of (2,2) doubles the size of units along the respective axis.

- (void)translateOriginToPoint:(NSPoint)aPoint

Translates an NSView’s coordinate system so that its origin has the coordinates (aPoint.x, aPoint.y).

- (void)rotateByAngle:(float)angle

Rotates an NSView’s coordinate system so that angle is the angle between the NSView’s coordinate system and its frame.

You can convert a point or rectangle from one NSView’s coordinate system to or from another NSView’s coordinate system with one of these methods:

- (NSPoint)convertPoint:(NSPoint)aPoint 
                  fromView:(NSView *)aView
- (NSPoint)convertPoint:(NSPoint)aPoint 
                  toView:(NSView *)aView
- (NSSize)convertSize:(NSSize)aSize 
                  fromView:(NSView *)aView
- (NSSize)convertSize:(NSSize)aSize 
                  toView:(NSView *)aView
- (NSRect)convertRect:(NSRect)aRect 
                  fromView:(NSView *)aView
- (NSRect)convertRect:(NSRect)aRect 
                  toView:(NSView *)aView

If you supply nil as an argument to any of the methods that take aView as an argument, the methods will convert to or from window coordinates.

There are many, many more methods — consult the NSView documentation for further information.

Moving and Resizing Views

You can move the position of an NSView relative to its superview’s coordinate system. This usually has the effect of changing where the NSView draws itself inside the window.

The following methods control the placement and movement of an NSView:

- (void)setFrameOrigin:(NSPoint)newOrigin

Moves the origin of the NSView’s frame to a precise position in its superview’s coordinate system

- (void)setFrameRotation:(float)angle

Rotates an NSView’s frame to an absolute position

- (void)setFrame:(NSRect)frameRect

Repositions and resizes an NSView within its superview’s coordinate system

- (void)setFrameSize:(NSSize)newSize

Resizes an NSView by an absolute amount in its superview’s coordinate system

Flipping

Views can be flipped, which means that the ordinal value of the y coordinate increases as it moves down the screen. Flipped coordinate systems are used for building subclasses of NSView such as the NSText object, which naturally move down from the upper-right corner. For these views, it’s easy to calculate the y coordinate by multiplying a line number by a constant.

The following NSView method deals with flipped views:

- (BOOL)isFlipped

NSView returns NO. NSView subclasses that need a flipped Y axis should override this method and return YES, in which case the Cocoa view mechanism will adjust accordingly.

Warning

Most application frameworks available for Microsoft Windows, X Windows, and Mac OS 9 operate only with flipped coordinate systems. If you are coming from these platforms, you may think it’s easier to simply flip the coordinate systems of the views that you create, rather than flipping your thinking. Resist this temptation! Many functions and NSView methods do not work the way that you would expect them to if the NSView’s coordinates are flipped. Flipped NSViews also seem to exercise some bugs within the AppKit. If you can avoid using flipped NSViews, we recommend that you do so.

The NSView Hierarchy

All views are arranged in a hierarchy. Each NSView has exactly one superview and can have zero to many subviews:

- (NSView *)ancestorSharedWithView:(NSView *)aView

Searches up the hierarchy for an NSView that is in common with the receiving NSView and aView.

- (id)viewWithTag:(int)aTag

Finds the nearest descendant NSView of the receiver that has aTag as its tag.

- (BOOL)isDescendantOf:(NSView *)aView

Returns whether or not the receiver is a descendant of aView.

- (void)setPostsFrameChangedNotification:(BOOL)flag

If flag is YES, this method causes the view to post a FrameChangedNotification when its frame changes. This is used with the NSScrollView to automatically update the size of the scrollbars. This notification is on by default; you should turn it off only for very special, temporary circumstances.

- (void)replaceSubview:(NSView *)oldView with:(NSView *)newView

If oldView is a subview of the receiver, it is removed from the NSView hierarchy and replaced with newView.

- (NSArray *)subviews

Returns an NSArray containing the NSView’s subviews. Do not modify this list directly.

- (NSView *)superview

Returns the NSView’s superview.

Opaque and Nonrectangular Views

Cocoa represents views by rectangular regions on the screen, but nothing forces the drawing that an NSView does to be rectangular. A drawing can be an odd shape, and it can even have holes through which you can see what’s behind it.

Each NSView specifies whether or not it completely fills its frame when it is drawn (so that you can’t see anything behind the NSView). If your NSView has holes in it or does not completely set every pixel within its frame, isOpaque should return NO. It is important to set this return value properly to reflect what your NSView does; this minimizes the amount of redrawing that needs to be done when your views are redisplayed.

These methods help you manage opaqueness:

- (BOOL)isOpaque

NSView returns NO by default. If your NSView subclass completely fills its bounds when it is drawn, you should should override this method and return YES.

- (NSView *)opaqueAncestor

Returns the NSView’s nearest ancestor NSView that is opaque. If the NSView is opaque, it will return self.

When the mouse is clicked in your NSView, the NSWindow object uses the hitTest: method to determine whether or not the NSView was clicked. You can override this method if parts of your NSView should not be mouse-sensitive — for example, if your NSView displays itself as a triangle.

- (NSView *)hitTest:(NSPoint)aPoint

Returns the lowest subview in the view hierarchy of NSViews that contains aPoint. The NSWindow class uses this method to determine in which NSView a mouseclick occurs. You can subclass this method to make some parts of your NSView “invisible” to the mouse.

Controlling Display and Redisplay

Two methods are used in display:

- (void)display

Causes the NSView to redisplay itself and its subviews by locking focus on itself and calling displayRect:. You should almost never call this method yourself; call setNeedsDisplay: instead.

- (void)displayRect:(NSRect)rect

Redisplays the portion of the NSView and its subviews specified by the argument rect.

Most Cocoa views need to redisplay themselves when something about their internal state changes. For example, an NSTextField object needs to redisplay itself when the contents of the NSTextField change. If you write your own custom NSView, you may override these methods to improve drawing performance under certain circumstances.

The following methods are used for managing the redisplay of views:

- (void)displayIfNeeded

Displays the receiving NSView and any of its subviews that need to be redisplayed.

- (BOOL)needsDisplay

Returns YES if the receiving view needs to be redisplayed.

- (void)setNeedsDisplay:(BOOL)flag

Tells the NSView that it needs to be redisplayed when flag = YES. Views that need to be redisplayed are automatically sent a display message each time the NSApplication class finishes handling an event. Thus, multiple actions that might cause a view to require displays may result in a single display call’s being dispatched, which increases efficiency.

- (void)setNeedsDisplayInRect:(NSRect)invalidRect

Tells an NSView that a region of itself and its subviews is no longer valid and needs to be redisplayed. This is used by the NSScrollView class. You can use it to improve drawing performance if you know that only part of a view needs to be redrawn.

Tip

Mac OS X Version 10.1 does not implement optimal redraw algorithms in the Application Kit. As a result, if you develop an application under Version 10.1, you will find that your NSView subclasses end up being displayed and redisplayed far more often than necessary. Nevertheless, you should still use the needsDisplay/setNeedsDisplay:/setNeedsDisplayInRect: architecture outlined above. When Apple addresses the bugs in the AppKit, your programs will run faster without additional modification.

Resizing

When a window is resized, the NSWindow class automatically sends a resizeSubviews: method to the NSWindow’s content view. The resizeSubviews: method is then passed down through the view hierarchy, resizing or not resizing the subviews as necessary.

Normally, you control resizing with IB’s Autosizing Info dialog. But there are times that you might want to catch resize events and do something special. Here are the methods used by Cocoa’s resizing machinery:

- (void)resizeSubviewsWithOldSize:(NSSize)oldFrameSize

Informs the NSView’s subviews that the NSView’s size has been changed from oldFrameSize.

- (void)setAutoresizesSubviews:(BOOL)flag

Makes an NSView automatically resize its subviews when it is resized.

- (void)setAutoresizingMask:(unsigned int)mask

Controls how an NSView resizes when its superview is resized.

- (void)viewWillStartLiveResize , - (void)viewDidEndLiveResize , - (BOOL)inLiveResize

Control “live resizing,” in which the window’s contents visibly change as the window is resized. By informing the view when live resizing is taking place, you can have the view do a “quick-and-dirty” draw operation during live resizing, and then do an “expensive-but-clean” draw operation when the live resize is finished.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.77.153