Is there any suitable way to deal with overlapping siblings of NSView?

I am working on a Cocoa application and I came across a situation where I would like two NSView objects to overlap. I have a parent NSView that contains two subqueries (NSView A and NSView B), each of which can have several subnets of its own.

Is there a way to handle this overlap? NSView B will always be “above” NSView A, so I want the overlapping parts of NSView A to be masked.

+16
cocoa
Jan 21 '09 at 17:44
source share
5 answers

If your application has a size of 10.5, enable layers for views, and it should just work.

If you mean support for 10.4 and below, you need to find a way to not overlap views, because matching sibling views are undefined behavior. As stated in the programming guide:

For performance reasons, Cocoa does not provide forced clipping among siblings' views or guarantees the correct invalidation and drawing behavior when siblings match. If you want the view to be facing a different view, you must make the front view a subview (or descendant) of the rear view.

I saw some hacks that can sometimes do this curiously, but there is nothing to rely on. You need to either make View A a subspecies of View B, or make one gigantic view that will fulfill both of its responsibilities.

+6
Jan 21 '09 at 19:08
source share

Chris, the only solution is to use CALayers. This is definitely the only solution.

OSX NSViews (September 2010) are easy to use: siblings do not work properly. One or the other will be randomly displayed on top.

To repeat, the problem is siblings .

To test this: using NSViews and / or nsimageviews. Make an application with a view that is one large image (1000x1000). In the view, place three or four small images / NSView here and there. Now add another 1000x1000 large image. Build and run the application again - you will see that it is simply broken. Often the lower (small) layers appear on top of a large cover layer. if you enable layer support in NSViews, it will not help, no matter what combination you try. So, the final test.

You need to give up NSViews and use CALayers and what it is.

The only annoyance with CALayers is that you cannot use IB to customize your material. You must set all the positions of the layer in the code,

yy = [CALayer layer]; yy.frame = CGRectMake(300,300, 300,300); 

Make only one NSView, which is only designed to hold your first CALayer (possibly called "back"), and then just put all your CALayers in the 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 works perfectly, you can nest and group everything you want, and it all works flawlessly with everything that should be correctly shown above / below everything that should appear above / below, regardless of the complexity of your structure. You can later do something for the layers or shuffle things differently,

 -(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 zero-second wrapper for everything you do is to prevent the animation that is provided to you for free - if you don't want the animation!)

By the way, in this quote from Apple

For performance reasons, Cocoa does not force clipping to sibling views or guarantees invalidation and drawing behavior when sisters match.

Their next suggestion ...

If you want to front view, you must make a front view a subview (or descendant) of the rear view.

Pretty much pointless (you can't replace siblings with subtitles, and the obvious error described in the above test still exists).

So these are CALayers! Enjoy it!

+13
Sep 17 '10 at 21:02
source share

There is a way to do this without using CALayers , and the application I worked on can prove it. Create two windows and use this:

 [mainWindow addChildWindow:otherWindow ordered:NSWindowAbove]; 

To remove "otherWindow", use:

 [mainWindow removeChildWindow:otherWindow]; [otherWindow orderOut:nil]; 

And you probably want to take the window title bar with:

 [otherWindow setStyleMask:NSBorderlessWindowMask]; 
+3
Nov 02 '11 at 2:19
source share

To ensure that NSView B always overlaps NSView A , make sure you use the correct NSWindowOrderingMode when you add subview:

 [parentView addSubview:B positioned:NSWindowAbove relativeTo:A]; 

You should also remember that hidden parts of A will not be requested for redrawing if view B is 100% opaque.

If you move subviews, you also need to make sure that you call -setNeedsDisplayInRect: for the viewports that you open.

+1
Jan 21 '09 at 18:43
source share

As Nate wrote, you can use:

 self.addSubview(btn2, positioned: NSWindowOrderingMode.Above, relativeTo: btn1) 

However, the ordering of views is not respected as soon as you ask both views to redraw it through a call to "needDisplay = true"

Siblings will not receive a drawRect call, only direct view hierarchies will be.

Update 1

To solve this problem, I had to break deep, very deep. Probably a week of research and Ive extended my findings to several articles. The final breakthrough in this article: http://stylekit.org/blog/2015/12/24/The-odd-case-of-luck/

Update 2

Be careful, although the concept is hard to understand, but it works, and it works great. Here is the end result and code to support it, links to github repo, etc.: http://stylekit.org/blog/2015/12/30/Graphic-framework-for-OSX/

+1
Dec 04 '15 at 20:20
source share



All Articles