views:

184

answers:

1

I have a minor irritant in an NSCollectionView in which the NSCollectionViewItem's break up visually when I scroll the window.

The rate of breakup depends upon the rate of scrolling. For example, if I scroll slowly the breakup occurs more often. Fast scrolling less so. It appears that the problem is when the custom NSView of the NSCollectionViewItem I am using crosses the boundary of the viewable frame.

My NSView (custom view of the NSCollectionViewItem) has a very simple drawing algorithm - nothing too complex.

Essentially I create a frame within the dirtyRect of the drawRect method and create a few frames inside that:

-(void)drawRect:(NSRect)dirtyRect
{   
    NSRect mOuterFrame = NSMakeRect(dirtyRect.origin.x, dirtyRect.origin.y, 104, 94);
    NSRect mSelectedFrame = NSInsetRect(mOuterFrame, 2, 2);
    NSRect mRedFrame = NSInsetRect(mSelectedFrame, 2, 2);
    NSRect mInnerFrame = NSInsetRect(mRedFrame, 2, 2);

    NSBezierPath * mOuterFramePath = [NSBezierPath bezierPathWithRect:mOuterFrame];
    NSBezierPath * mSelectedFramePath = [NSBezierPath bezierPathWithRect:mSelectedFrame];
    NSBezierPath * mRedFramePath = [NSBezierPath bezierPathWithRect:mRedFrame];
    NSBezierPath * mInnerFramePath = [NSBezierPath bezierPathWithRect:mInnerFrame];

    [mainBackgroundColor set];
    [mOuterFramePath fill];

    if (selected)
        [[NSColor yellowColor] set];
    else
        [mainBackgroundColor set];

    [mSelectedFramePath fill];

    if (isRedBorder)
        [[NSColor redColor] set];
    else
        [mainBackgroundColor set];

    [mRedFramePath fill];

    [[NSColor blackColor] set];
    [mInnerFramePath fill];
}

I have tried locking the focus and releasing before and after the code as well as setting the graphics context and restoring it - none of which seem to solve the problem.

I am using Snow Leopard - not that I think that makes a difference.

Solution Update

For anyone interested here is the solution to the problem as recommended by NSResponder. I was creating the initial mOuterFrame based upon the drawRect: methods dirtyRect which, as pointed out, was the incorrect thing to do. A quick change from:

NSRect mOuterFrame = NSMakeRect(dirtyRect.origin.x, dirtyRect.origin.y, 104, 94);

To a 0 based origination point:

NSRect mOuterFrame = NSMakeRect(0, 0, 104, 94);

I also tweaked the efficiency of the code, being as I am using only rectangles, as suggested although the code change above was enough to resolve the problem in itself. I had to add a change to get a two pixel line. New method:

-(void)drawRect:(NSRect)dirtyRect
{   
    NSRect mOuterFrame = NSMakeRect(0, 0, 104, 94);
    NSRect mSelectedFrame = NSInsetRect(mOuterFrame, 2, 2);
    NSRect mRedFrame = NSInsetRect(mSelectedFrame, 2, 2);
    NSRect mInnerFrame = NSInsetRect(mRedFrame, 2, 2);

    [NSBezierPath setDefaultLineWidth:2.0];

    [mainBackgroundColor set];
    [NSBezierPath strokeRect:mOuterFrame];

    if (selected)
        [[NSColor yellowColor] set];
    else
        [mainBackgroundColor set];

    [NSBezierPath strokeRect:mSelectedFrame];

    if (isRedBorder)
        [[NSColor redColor] set];
    else
        [mainBackgroundColor set];

    [NSBezierPath strokeRect:mRedFrame];

    [[NSColor blackColor] set];
    [NSBezierPath strokeRect:mInnerFrame];
}
A: 

First thing I noticed is that your code above is rather wasteful, since you're creating a bunch of paths when you could just be using NSFrameRect() and NSRectFill() instead.

Secondly, the geometry of what you draw shouldn't be dependent on the rectangle passed to -drawRect:. That rectangle just tells you what the area to update is, so you can be more efficient in deciding what to draw if your view is complex.

NSResponder
I think you mean `NSFrameRect` not `NSDrawRect`.
Rob Keniger
NSResponder - thanks. That fixed the issue (not basing the initial outer frame on the dirtyRect). I also tweaked the code to be a bit more efficient making use of NSFrameRect. Thanks!
Hooligancat