Working Around Incorrect -needsToDrawRect: Behavior in Custom View Classes

NSView's -needsToDrawRect: convenience method, which first appeared in Mac OS X 10.3 Panther, incorrectly returns NO in some circumstances. This may result in incomplete drawing in a custom view class that relies on -needsToDrawRect: to determine which parts of its content it needs to draw.

This TechNote explains how to work around the incorrect behavior, which is fixed in Mac OS X 10.4 Tiger.





IMPORTANT: NSView's -needsToDrawRect: has been fixed in Mac OS X 10.4 Tiger. The workaround listed below is not necessary for software running on 10.4 or later. It remains necessary for developers who need to target Mac OS X 10.3 Panther.

Background

Mac OS X 10.3 introduced a more optimized drawing model for Cocoa views. Supported by new NSView methods -getRectsBeingDrawn:count: and -needsToDrawRect:, this model enables a view to more precisely constrain its drawing to only those parts of its content area that need to be updated. Use of these new methods is discussed in detail in the View Programming Guide for Cocoa, Constraining Drawing to Improve Performance.

The -needsToDrawRect: method is provided as a convenient means for a view's -drawRect: implementation to check whether some particular object in its content area needs to be drawn. Given the object's bounding rectangle, -needsToDrawRect: should return YES if this rectangle intersects any part of the view that needs redisplay.

Back to Top 

The Problem

As an efficiency measure, NSView's -needsToDrawRect: implementation first tests whether the rectangle it is given intersects the bounding box of the area being drawn. In some cases, most often when text is being drawn in the view, this bounding rectangle can be incorrectly computed causing -needsToDrawRect: to erroneously return NO. This errant behavior can cause drawing that is conditioned on the result of -needsToDrawRect: to be incorrectly suppressed.

Back to Top 

Overriding -needsToDrawRect: to Perform Correctly

You can work around this problem by overriding -needsToDrawRect: to perform as intended, in any custom view class in which you use -needsToDrawRect:.

If a view is rarely asked to draw more than a few rectangles at a time, and its content is not particularly complex or expensive to draw, the following reimplementation of -needsToDrawRect: will suffice.

Listing 1: A Simple -needsToDrawRect: Replacement

- (BOOL)needsToDrawRect:(NSRect)rect
{
    const NSRect *rectList;
    int count;
    int i;

    [self getRectsBeingDrawn:&rectList count:&count];
    for (i = 0; i < count; i++) {
        if (NSIntersectsRect(rect, rectList[i])) {
            return YES;
        }
    }
    return NO;
}

The above approach has the advantage of not requiring any changes to code that calls -needsToDrawRect:.

To improve performance in cases where a view is typically given many rectangles to draw at once, you may choose to define and use an alternative to this method that quickly rejects objects that lie outside the rectList's bounding rect. You would invoke this method instead of -needsToDrawRect:, passing it -drawRect:'s rectangle parameter as its rectListBounds parameter.

Listing 2: A More Optimized -needsToDrawRect: Replacement

- (BOOL)needsToDrawRect:(NSRect)rect rectListBounds:(NSRect)rectListBounds
{
    const NSRect *rectList;
    int count;
    int i;

    if (!NSIntersectsRect(rect, rectListBounds)) {
        return NO;
    }
    [self getRectsBeingDrawn:&rectList count:&count];
    if (count == 1) {
        return YES;
    } else {
        for (i = 0; i < count; i++) {
            if (NSIntersectsRect(rect, rectList[i])) {
                return YES;
            }
        }
        return NO;
    }
}

Back to Top 

Document Revision History

DateNotes
2007-01-22Updated to reflect fixes in Mac OS X 10.4 Tiger.
2004-02-06Describes how to work around a bug in NSView's -needsToDrawRect: method.

Posted: 2007-01-22


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.