Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

drawrect Vs layoutsubviews - NSView - Cocoa touch

I can't find a decent answer to explain me the difference between these 2 functions. when does every one gets called, and how does one different then the other ?

for example , can't I just layout my views inside drawrect ?

Thanks

like image 441
Idan Avatar asked Sep 24 '10 17:09

Idan


1 Answers

-layoutSubviews is called from -layoutIfNeeded if the "layout needed" flag was set (using -setNeedsLayout, or automatically when the view bounds changes). Use it for positioning your view [EDIT: Use it for positioning subviews].

-drawRect: is called from -displayIfNeeded if the "display needed" flag was set (using -setNeedsDisplay, or automatically if you set view.contentMode = UIViewContentModeRedraw).

Both -layoutIfNeeded and -displayIfNeeded are called automatically by UIKit/CoreAnimation before things are drawn to screen; you rarely need to call them directly.

You can position your subviews in -drawRect: (you can even add subviews!), but this is unwise:

  • By default, -setNeedsDisplay is not called automatically on a bounds change.
  • Implementing -drawRect: reduces performance (UIKit/CoreAnimation has to create a bitmap-backed graphics context for you); only do so if you need to perform custom drawing.
  • You need to redraw the view in -drawRect:. Drawing is expensive. Moving views around is cheap.
  • UIKit/CoreAnimation probably does a layout pass followed by a drawing pass. CoreAnimation can use layout information to decide what views need drawing (e.g. it could ignore views obscured by opaque subviews, off-screen views, or subviews outside the bounds of a clipsToBounds=YES view; or it could only draw a sub-rect of a large view). If you move views during the drawing pass, CoreAnimation might not draw them correctly.

EDIT: And some more detail when I'm awake:

What's the difference between "display" and "draw"? Displaying is done by-[CALayer display]; the default implementation is (approximately)

  • If the layer's delegate responds to -displayLayer:, call [self.delegate displayLayer:self]. -displayLayer: is supposed to set layer.content to (e.g.) a CGImage,
  • Otherwise, if the layer's delegate responds to -drawLayer:inContext:, set up a bitmap-backed context, call [self.delegate drawLayer:self inContext:context], and save the output to layer.content (the output is actually a CABackingStore, which is presumably a private API)
  • Otherwise, don't change layer.content.

The view is the layer's delegate, so you can implement -[MyView displayLayer:] instead, and do interesting stuff like

  • self.layer.contents = (id)([UIImage imageNamed:@"foo"].CGImage) (which is roughly what UIImageView does)
  • A no-op, to prevent any "drawing". This might be useful if you've subclassed e.g. UIToolbar and want to give it a transparent background. (This also prevents creation of the CGContext/CABackingStore.)
  • Move subviews around without the performance penalty (but it's still not a good idea for the reasons above).
like image 84
tc. Avatar answered Sep 20 '22 15:09

tc.