I have a layer-hosting view set up like this in a custom NSView
subclass:
[self setLayer:rootLayer];
[self setWantsLayer:YES];
I add all the sublayers to the layer tree after I called setNeedsDisplay
on each sublayer. Each layer's content is provided by a drawLayer:inContext
method of my layer's delegate.
Here is my problem:
After initializing my view the view gets draw correctly. However, when the model has changed and I call [myCustomView setNeedsDisplay:YES];
from my view controller the drawLayer:inContext
is not called.
I am confused now how to update the view:
- Do I have to call the
setNeedsDisplay
method on each CALayer in the layer tree? - Should not the call of
setNeedsDisplay:YES
on the layer-hosting view itself trigger the redraw of the whole layer tree?
Thanks for your help.
Edit
I have found something in the NSView Class reference
A layer-backed view is a view that is backed by a Core Animation layer. Any drawing done by the view is the cached in the backing layer. You configured a layer-backed view by simply invoking setWantsLayer: with a value of YES. The view class will automatically create the a backing layer for you, and you use the view class’s drawing mechanisms. When using layer-backed views you should never interact directly with the layer.
A layer-hosting view is a view that contains a Core Animation layer that you intend to manipulate directly. You create a layer-hosting view by instantiating an instance of a Core Animation layer class and setting that layer using the view’s setLayer: method. After doing so, you then invoke setWantsLayer: with a value of YES. When using a layer-hosting view you should not rely on the view for drawing, nor should you add subviews to the layer-hosting view.
In my case I have a layer-hosting view. So does that indeed mean that I have to trigger the redraw manually? Should I implement a pseudo drawRect method in the custom NSView to call the appropriate setNeedsDisplay on the CALayers that changed?
There is also an oft-used practice of having an empty "drawrect", a la
-(void) drawRect:(NSRect)dirtyRect {}
to help coerce things into drawing, i believe via good oleview.needsDisplay = YES;
.and it should be noted.. that what is indeed happening is that - by saying your
NSView *view;
islayer.delegate = view;
causes the layer to be drawn when[layer setNeedsDisplay];
is called.... via- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {...}
..along the same vein... when saying
layer.layoutManager = view
... subsequent demands that[layer setNeedsLayout];
will be fulfilled only when the- (void) layoutSublayersOfLayer:(CALayer *)layer {..}
method is implemented..These vital concepts are glossed over / strewn about in Apple's docs... and they are really so pivotal to making absolutely anything work at all.
After further research in Apple's sample code of a kiosk-style menu I found out that if you are using a layer-hosting view, you have to take care of the screen updates which are neccessary due to model changes yourself. Calling
setNeedsDisplay:YES
on the NSView will not do anything.So what one has to do if one has to update a view one should write a method like reloadData and in it one should call
setNeedsDisplay
on each CALayer that needs a refresh. I am still not sure if a call to this method on the root layer will propagate through all the children layers but I do not think so.I solved the problem now by calling
setNeedsDisplay
on the individual CALayers that needed recaching. It works without problems.