10

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?

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165

5 Answers5

10

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

corbin dunn
  • 2,647
  • 1
  • 18
  • 16
  • 2
    My observations on this topic match with @uliwitness answer. There are caveats when you mix and match layer-backed and non-layer backed views. – SayeedHussain Aug 26 '14 at 08:43
  • Unfortunately they don't work if they are not layered. I try to make a hole in CEF (chromium embedded framework) window with help of an additional NSView, so it is not rendered after it. (it seems CEF internally uses OpenGL or another magic). The only way I found is to make my NSView layered and leave that CEF a usual one – dev_null Oct 31 '14 at 14:47
  • 4
    Uli, this is the citation :) – corbin dunn May 27 '15 at 14:54
8

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.

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
  • Non-layer backed NSView siblings are not allowed to overlap. See my answer. – nschmidt May 09 '15 at 15:13
  • @nschmidt Interesting. Do you possibly have a sample app that shows that it's problematic to have non-layer-backed overlapping sibling NSViews? Two (ex-) Apple engineers are saying it's fine since 10.5 and in my tests it seems to be true. – Johannes Fahrenkrug May 11 '15 at 12:59
  • I have uploaded a simple demo to [github](https://github.com/nsc/SiblingViews.git). There is a scroll view in the background overlapped by a couple of views. When you scroll, everything is messed up. – nschmidt May 12 '15 at 13:31
  • @nschmidt That's excellent, thank you. From my experience NSScrollView seems to be a beast of its own. NSScrollView has a whole internal view hierarchy and it indeed doesn't play well with overlapping sibling views when they are not layer-backed. Other not-so-complex views overlap just fine, though. – Johannes Fahrenkrug May 12 '15 at 15:43
4

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.

uliwitness
  • 8,532
  • 36
  • 58
  • Interesting! I didn't even notice that they didn't explicitly mention layers. However, Core Animation has only been around since 10.5, so there were no layer-backed views before that. Are you sure that overlapping works reliably with non-layer-backed views? That's the first time I hear that (would be awesome, though!). This might be relevant too: http://stackoverflow.com/questions/466297/is-there-a-proper-way-to-handle-overlapping-nsview-siblings – Johannes Fahrenkrug May 23 '12 at 13:09
  • I do not say anywhere that overlapping works with non-layer-backed views. It definitely did *not* work in older versions (though it usually looked like it worked, it broke in random cases, there was something very nondeterministic about overlapping non-layer-backed views) – uliwitness Jan 08 '14 at 10:03
  • Oh, it seems I did say it "works since 10.5 for non-layered". :-S – uliwitness Aug 27 '14 at 16:42
2

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.

Gobra
  • 4,263
  • 2
  • 15
  • 20
  • That's right, if you're animating a sibling NSView (which is overlapping another NSView) then you will see flickering and weird behavior. You need to then update all views during animation if you want it to look right. – strange Jul 09 '13 at 23:59
2

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];
nschmidt
  • 2,383
  • 16
  • 22