views:

113

answers:

1

I'm confused about when -setNeedsDisplay should be called by me. As an example, for updating a button with different images between presses (toggling between the two), the example code from Apple shows -setImage being called on a UIButton. There's no call to -setNeedsDisplay that I can find after that, though. So do UIControl methods all perform a -setNeedsDisplay internally, and thus -setNeedsDisplay is only to be used when manipulating instances of UIView? Or are there more specific semantics? If I subclass UIView, should my methods always handle redrawing internally? How/where is this specified in the docs?

From LevelView.m in BubbleLevel source code:

- (void)toggleHoldButton:(id)sender {
    if (holdButtonIsShowing == YES) 
    {
        holdButtonIsShowing = NO;
        [holdButton setImage:[UIImage imageNamed:@"release_button.png"] forState:UIControlStateNormal];
    } else 
    {
        holdButtonIsShowing = YES;
        [holdButton setImage:[UIImage imageNamed:@"hold_button.png"] forState:UIControlStateNormal];
    }    
}
A: 

setNeedsDisplay is used for re-rendering or re-drawing the view (An UIView or UIView subclass instance). From our point of view: it calls drawRect: but actually before that it prepares the context and makes the view ready to be re-drawn. (That is why you shouldn't call drawRect: directly)

A usual case is when you have implemented drawRect: method (some examples: drawing contents with OpenGL in a view or drawing text using CoreText or drawing some graphics using CoreGraphics, etc) if you want to update the drawing at a certain moment explicitly you call setNeedsDisplay and it will call drawRect: and you view will be redrawn right away. If not, drawRect: method will be called when the bounds of the view changed and in some other situations when the system decides it is necessary to redraw the view.

In the case of a button, you don't need to do that because surely when the button is pressed a internal call to setNeedsDisplay will be done making the button to change, etc.(Who really knows what happens in the background. The thing is you don't have to worry about drawRect: when using UIButtons in that manner)

You can also change the appearance of a view by moving its sublayers or subviews, and that does not necessarily imply redrawing the view. This is the case when you view acts like a container and not as a drawing object.

A good rule could be that you don't need to call setNeedsDisplay explicitly if you didn't implement drawRect: method.

http://developer.apple.com/iphone/library/documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/drawRect:

Hope it helps.

;)

UPDATE: 2009/09/09

the way UIButton calls drawRect: depends on its implementation and we are supposed not to care about it(at least not if we are not subclassing). But if you really know it you can subclass it and override :

- (void) drawRect:(CGRect)frame{
    NSLog(@"%s is being called!!!", _cmd);
    [super drawRect:frame];
}

and now from another object try loading it, resizing it, moving it, scrolling it(If available),hiding it and see the debug console (Cmd + Shift + R) I would recommend to do that not only for a UIButton but also for a normal UIView. That will surely give a clear idea when drawRect: is called. ;) (Also other methods like layoutSubviews:, etc)

The reason I said we are not supposed to care about drawRect: in UIButton is because we don't need it, we preset an image for a certain state using setImage:forState: and that is all. We can re-set this images any time and the button will change its image as needed and it might redraw its view depending on the way apple engineers designed it.

Regarding your question if setNeedsDisplay conforms the rule I gave you. I believe yes. I put a breakpoint at the beginning of every method and nothing and checked what happened when setNeedsDisplay was called. None of the methods of LeverView were called!. then I commented the 3 calls of setNeedsDisplay and the results were the same!. (iOS4.1) Hence those three lines of code are not needed. ;)

You have to be careful when calling setNeedsDisplay because depending in your implementation it could make your program very slow! since is gonna redraw everything and probably is not necessary. ;)

Pheeew... lol. Quite a long post only about drawRect:

Cheers

nacho4d
Thanks so much. For my own edification though, if for instance I were to try to recreate the UIButton class, I still am curious about when exactly drawRect: is happening. Is it 1) The target action gets called (where button elements could be told to update via for instance -setImage:) and then control returns to the caller which does a single setNeedsDisplay? Or 2) does each message such as setImage: sent to UIButton objects themselves involve a redisplay? Also for that rule of thumb, does it encompass the call to setNeedsDisplay in that same example inside setupSubviewsWithContentFrame: ?
snackdefend
I updated my post ;) please check it.
nacho4d
Thank you very much, that subclassing idea is great, trying it now. I appreciate the setNeedsDisplay clarification; I had the same results after commenting them out as well. I assume the call inside of LevelView's init is just in the hopes that the scheduler will begin drawing as soon as possible after the app is launched.
snackdefend