Search code examples
macoscocoacore-animationcalayer

CALayer random ordering in Cocoa (Mac OS)


This bug is driving me crazy... Each time I run my app from Xcode, my panel draws it's layers in a random order! Even though the subviews order is intact!!

The app is a status bar app, that displays a simple NSPanel when the user clicks the status item. The NSPanel is loaded from the nib, and I can't say I do any more than that!!

Am I doing anything weird with the CALayers? Absolutely not... All I have in KMStatusPanelView is an overriden -[NSView drawRect:]. The KMRootView has the following:

In KMRootVC, which is the view controller of KMRootView, I have:

- (void)awakeFromNib
{
    [super awakeFromNib];

    NSAssert(self.view.layer, @"Layer not created");
    [self.view.layer setCornerRadius:18.0];
    [self _loadControllerAtIndex:0];
}

.. and in KMRootView, I override -[NSView drawRect:], and also have:

- (void)awakeFromNib
{
    [super awakeFromNib];

    NSAssert(self.layer, @"Layer not found");
    [self.layer setMasksToBounds:YES];
}

That's it! That's all the code related to the Core Animation layer, with the exception of the fact that I ticked the checkmarks in IB to create the layers for me, instead of calling -[NSView setWantsLayer:].

Here is a log from two different runs (I have removed most of the irrelevant data):
Obviously, KMRootView should be at the bottom, as you can see from the -[NSView subviews] output.

Run with correct results:

$2 = 0x000000010029b260 <__NSArrayM 0x10029b260>(
<KMRootView: 0x10029c400>,
<KMStatusPanelView: 0x100248a30>
)

(lldb) po [[[self.window contentView] layer] sublayers]
$3 = 0x0000000100264e50 <CALayerArray 0x100264e50>(
<_NSViewBackingLayer:...; bounds = CGRect (0 0; 320 480); delegate = <KMRootView: 0x10029c400>; sublayers =...,
<_NSViewBackingLayer:...; bounds = CGRect (0 0; 320 488); delegate = <KMStatusPanelView: 0x100248a30>;...
)

Run with screwed up results:

$0 = 0x000000010023aa40 <__NSArrayM 0x10023aa40>(
<KMRootView: 0x102430f70>,
<KMStatusPanelView: 0x102430660>
)

(lldb) po [[[self.window contentView] layer] sublayers]
$1 = 0x000000010242d2c0 <CALayerArray 0x10242d2c0>(
<_NSViewBackingLayer:...; bounds = CGRect (0 0; 320 488); delegate = <KMStatusPanelView: 0x102430660>;...,
<_NSViewBackingLayer:...; bounds = CGRect (0 0; 320 480); delegate = <KMRootView: 0x102430f70>; sublayers = ...
)

Solution

  • Don't use IB to enable layer backed views nowhere in hierarchy, but in awakeFromNib call setWantsLayer:YES