views:

60

answers:

1

I have in front of me two Quartz iPhone apps. In each of them, calls to setNeedsDisplay cause a view to redraw itself. But there is an important difference. In one case (the "Quartz Fun" app from the Mark/Lamarche book "Beginning iPhone development"), the view starts out blank each time. In the other case (the app I am working on), the view starts with whatever was there before, and new graphics are added on top of it.

I can't figure out why this is. Can anyone clue me in?

Thanks.

EDIT #2: I still don't understand what is going on here. But I have figured out that I can clear my view by calling

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, self.frame);

EDIT #3: showing shortened code:

As a suggested by a commenter, I have edited my app so that the issue occurs with very little code. [The form of the issue is a bit different now, as explained below.]

App delegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  
    DiceView *dv = [[DiceView alloc]initWithFrame: window.frame];
    [window addSubview:dv];

    [dv release];
    [window makeKeyAndVisible];
    return YES;

}

DiceView:

- (void)drawRect:(CGRect)rect {
    static int nDrawrectCalls = 0;
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 1.0);
    CGContextSetStrokeColorWithColor(context, (nDrawrectCalls%5==0?[UIColor redColor]:[UIColor greenColor]).CGColor);
    CGContextMoveToPoint(context, 10, 30+10*nDrawrectCalls);
    CGContextAddLineToPoint(context, 300, 30+10*nDrawrectCalls);
    CGContextStrokePath(context);
    nDrawrectCalls++;
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {  
    [self setNeedsDisplay];
}

Everything else is just default stub methods.

Now for the difference. It now appears to start drawing with whatever was on the screen two touches prior. In other words, after touch #2, I see the initial line, plus the line from touch #2 -- but not the line from touch #1. After touch #3, I see the lines from touches #1 and #3. After touch #4, I see the initial line and the lines from touches #2 and #4. And so on.

+1  A: 

UIView has a clearsContextBeforeDrawing boolean property that switches between the different behaviors you describe. The default is YES which means the view empties the context before calling drawRect:

Check if that property is set somewhere in the example.

Apparently a view doesn't clear it's context correctly, when no backgroundColor is set on the view. Please set backgroundColor to something other than nil.

I think this is a bug and have filed rdar://8165730.


EDIT

It's not a bug. You have to set the opaque property to NO or the backgroundColor. The behavior is described in the UIView Documentation.

Property clearsContextBeforeDrawing:

The default value of this property is YES. When set to YES, the current graphics context buffer in the drawRect: method is automatically cleared to transparent black before drawRect: is invoked. If the view’s opaque property is also set to YES, the backgroundColor property of the view must not be nil or drawing errors may occur.

If the value of this property is NO, it is the view’s responsibility to completely fill its content. Drawing performance can be improved if this property is NO—for example, when scrolling.

Property opaque:

YES if it is opaque; otherwise, NO. If opaque, the drawing operation assumes that the view fills its bounds and can draw more efficiently. The results are unpredictable if opaque and the view doesn’t fill its bounds. Set this property to NO if the view is fully or partially transparent. The default value is YES.

tonklon
Excellent point . . . but I checked, and the property is set to YES. However, it does not clear.
William Jockusch
Are you calling drawRect: directly? Are you calling setNeedsDisplay on a background thread? Are you calling any UIKit methods or functions on a background thread? Are you doing something with this views layer?If all answers are NO: Try to reduce your code to the bare minimum that's required to reproduce the issue. Only a appDelegate that adds the view to the window and the view calling setNeedsDisplay in touchesEndedWithEvent:
tonklon
Shortened code above.
William Jockusch
try to set the backgroundColor, see above
tonklon
Setting the backgroudColor did the trick, thanks!
William Jockusch