Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Layer hosting NSView within NSOutlineView

I am trying to create a custom NSView that hosts a CALayer hierarchy to perform efficient display. This NSView is then embedded within a NSTableCellView that is displayed by a View-Based NSOutlineView.

The problem is that whenever I expand or collapse an item, all rows are being moved, but the layer's content remains displayed at the position it was before changing the outline.

Scrolling the NSOutlineView seems to refresh the layers and they resync with their rows at that point.

I have debugged this behavior using Instruments and it seems that the scrolling provokes a layout operation which updates the layers with a setPosition: call that should have occured when expanding or collapsing items.

Here is some sample code for a simple layer hosting NSView subclass.

@interface TestView : NSView

@end

@implementation TestView

- (instancetype)initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];
    CAShapeLayer* layer = [CAShapeLayer layer];
    layer.bounds = self.bounds;
    layer.position = CGPointMake(NSMidX(self.bounds), NSMidY(self.bounds));
    layer.path = [NSBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
    layer.fillColor = [NSColor redColor].CGColor;
    layer.delegate = self;
    self.layer = layer;
    self.wantsLayer = YES;
    return self;
}

@end

I have tried a lot of potential solutions to this problem but I couldn't find any interesting method that gets called on the NSView instance that could be overriden to call [self.layer setNeedsDisplay] or [self.layer setNeedsLayout]. I also tried various setters on the CALayer itself such as :

layer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
layer.needsDisplayOnBoundsChange = YES;
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;

Can anyone help me figure out how to make this layer display properly inside a NSOutlineView?

like image 693
Dalzhim Avatar asked Mar 17 '15 20:03

Dalzhim


1 Answers

I ended up answering my question. The problem wasn't in the way my TestView was implemented. I simply missed one of the steps for enabling CoreAnimation support within the application. The relevant reference is within the Core Animation Programming Guide.

Basically, in iOS Core Animation and layer-backing is always enabled by default. On OS X, it has to be enabled this way :

  1. Link against the QuartzCore framework
  2. Enable layer support for one or more of your NSView objects by doing one of the following
    • In your nib files, use the View Effects inspector to enable layer support for your views. The inspector displays checkboxes for the selected view and its subviews. It is recommended that you enable layer support in the content view of your window whenever possible
    • For views you create programmatically, call the view’s setWantsLayer: method and pass a value of YES to indicate that the view should use layers.

Once I enable layer support on any of the NSOutlineView's parents, the various glitches are solved.

like image 181
Dalzhim Avatar answered Nov 03 '22 09:11

Dalzhim