I'm working on a Cocoa application, and I've run into a situation where I would like to have two NSView objects overlap. I have a parent NSView which contains two subviews (NSView A and NSView B), each of which can have several subviews of their own.
Is there a proper way to handle this kind of overlap? NSView B would always be "above" NSView A, so I want the overlapped portions of NSView A to be masked out.
Chris, the only solution is to use CALayers. That is definitely the one and only solution.
NSViews are plain broken in OSX (Sept 2010): siblings don't work properly. One or the other will randomly appear on top.
Just to repeat, the problem is with siblings.
To test this: using NSViews and/or nsimageviews. Make an app with a view that is one large image (1000x1000 say). In the view, put three or four small images/NSViews here and there. Now put another large 1000x1000 image on top. Build and launch the app repeatedly - you'll see it is plain broken. Often the underneath (small) layers will appear on top of the large covering layer. if you turn on layer-backing on the NSViews, it does not help, no matter what combo you try. So that's the definitive test.
You have to abandon NSViews and use CALayers and that's that.
The only annoyance with CALayers is that you can't use the IB to set up your stuff. You have to set all the layer positions in code,
yy = [CALayer layer];
yy.frame = CGRectMake(300,300, 300,300);
Make one NSView only, who's only purpose is to hold your first CALayer (perhaps called 'rear'), and then just put all your CALayers inside rear.
rear = [CALayer layer];
rear.backgroundColor = CGColorCreateGenericRGB( 0.75, 0.75, 0.75, 1.0 );
[yourOnlyNsView setLayer:rear]; // these two lines must be in this order
[yourOnlyNsView setWantsLayer:YES]; // these two lines must be in this order
[rear addSublayer:rr];
[rear addSublayer:yy];
[yy addSublayer:s1];
[yy addSublayer:s2];
[yy addSublayer:s3];
[yy addSublayer:s4];
[rear addSublayer:tt];
[rear addSublayer:ff];
everything then works utterly perfectly, you can nest and group anything you want and it all works flawlessly with everything properly appearing above/below everything it should appear above/below, no matter how complex your structure. Later you can do anything to the layers, or shuffle things around in the typcial manner,
-(void) shuff
{
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:0.0f]
forKey:kCATransactionAnimationDuration];
if ..
[rear insertSublayer:ff below:yy];
else
[rear insertSublayer:ff above:yy];
[CATransaction commit];
}
(The only reason for the annoying 'in zero seconds' wrapper for everything you do, is to prevent the animation which is given to you for free - unless you want the animation!)
By the way in this quote from Apple,
For performance reasons, Cocoa does not enforce clipping among sibling views or guarantee correct invalidation and drawing behavior when sibling views overlap.
Their following sentence ...
If you want a view to be drawn in front of another view, you should make the front view a subview (or descendant) of the rear view.
Is largely nonsensical (you can't necessarily replace siblings with subs; and the obvious bug described in the test above still exists).
So it's CALayers! Enjoy!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With