11
votes

I'm a little confused. The Apple Documentation states this:

Note: For performance reasons, Cocoa does not enforce clipping among sibling views or guarantee correct invalidation and drawing behavior when sibling views overlap. 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.

So according to this, sibling views should not overlap or else the behavior is undefined.

In the Cocoa Slides demo app, however, layer-backed NSView siblings do overlap and it seems to work just fine:

Cocoa Slides screenshot

So is the Cocoa Slides sample code wrong and it's just a coincidence that it works, or is the documentation out of date? Out of date since 10.5, that is?

5

5 Answers

10
votes

Overlapping views work fine, layer backed or not, on Leopard and higher.

8
votes

After some research, it seems that the Apple Documentation is indeed out of date.

Layer-backed NSView siblings are allowed to overlap since 10.5.

This discussion from 2009, involving Apple engineers David Duncan and Corbin Dunn finally provides some clear answers:

Overlapping views work on Leopard, but do not work before that. The documentation is out of date.

I have a group of views, each one with many smaller views inside, which need to be presented in an overlapping manner over the same rect on a window, so that they can be seen through each other. In my preliminary tests, I made each big view a sibling of a single background view. Was planning on bringing each one to the front, when necessary by re-arranging the z-order. Is there any future (or present) in this appoach?

That will work on Leopard.

Source: http://www.cocoabuilder.com/archive/cocoa/228191-nsview-behaves-different-on-10-4-vs-10-5.html#228983

Update: The James Dempsey also replied on Twitter:

My understanding is that overlapping sibling views are OK as of 10.5, layer-backed or not.

4
votes

Layer-backed views are layered by OpenGL (well, the Quartz compositor, but it helps to think of each layer as a polygon with an OpenGL texture on it), so they've always supported correct overlapping.

The thread on CocoaBuilder/Cocoa-Dev doesn't mention layers at all. Which means it talks about regular NSViews without a backing CALayer (or rather, with only a CALayer for the entire window).

One exception mentioned is OpenGLView (again, without layers), which always composited its OpenGL rectangle on top of the window, obliterating any subviews. I don't think making an NSOpenGLView layer-backed works, but an OpenGL layer can be used instead, which will be composited correctly between the other layers.

Another exception are layers on top of non-layer-backed views, which makes sense, because all non-layer-backed views effectively inhabit a single layer, which is of course below any of its sub-layers (which layer-backed views hosting in a non-layer-backed parent view would have to be).

So in short, it works since 10.5 for non-layered, and since forever for layer-backed views, with caveats when you mix-and-match or use OpenGL.

PS - I'm not 100% sure the statement on overlapping non-layer-backed views should be taken as canonical, though. It's an informal statement made by an Apple engineer. Things could have changed and bugs could have been discovered that make things not work. I generally use layers when I want correct overlapping.

2
votes

It might be useful to someone: I had an issue with flickering overlapped non-layered subviews on MacOS 10.7+. In my app, the views were used to render some info about selected graphics object (selection frame, scaling control points etc.), so they had some animation - that's they key in my case.

It seems like overlapping siblings really works fine even without layer, but in simpler cases. I had a bunch of animated views, each with it's own timer - and it flicked. I have found two solutions: either turn on layers or synchronize the animation, switching to a single shared timer and updating all the views at the same moment.

At least this trick helped in my app, since I didn't want to use the layers.

2
votes

NSView siblings are generally allowed to overlap. One thing that might hit you though is how NSScrollView works by default. When you have a vanilla NSScrollView overlapped by sibling views things break.

That is because of NSClipView that only draws the part of the view that is scrolling in and copies the stuff that didn't change. When you have sibling views that overlap the scroll view this optimization doesn't work and the views seem to scroll even though they are only siblings.

To make overlapping sibling views work even when they are not layer-backed you therefore have to disable this optimization:

[scrollView.contentView setCopiesOnScroll:NO];