2

Here is the issue: a view-based NSOutlineView (or NSTableView, both have the issue) contains a custom control hosting a CALayer, for custom animation purposes. When resizing the outline view, or after deleting rows (animated deletion), the CALayer draws at the wrong place.

This is just after an horizontal resize (the gray square is the CALayer):

This is just after an horizontal resize

This is just after deleting the row where the gray square is missing (the gray square is the CALayer):

This is just after deleting the row where the gray square is missing

It looks like the CALayer is drawn a little later than the view hierarchy itself, and in the case of the row deletion, isn't redrawn/removed at all. Here is a little video: http://cl.ly/image/2E2X1Y3W2s3O

The code is basically:

// MyButton.m

- (id)initWithFrame:(NSRect)frame
{
    /...
        [self setLayer:[[MyCALayer alloc] init]];
        [self setWantsLayer:YES];
        [self.layer setDelegate:self];

        [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
        [self setLayerContentsPlacement:NSViewLayerContentsPlacementCenter];
    }
    return self;
}

- (void)viewDidChangeBackingProperties
{
    [super viewDidChangeBackingProperties];
    [[self layer] setContentsScale:[[self window] backingScaleFactor]];
}

// MyCALayer.m

- (id)init  // ...
- (id)initWithLayer:(id)layer // ...

- (void)drawInContext:(CGContextRef)context
{
    // draw a simple rect
}

+ (BOOL)needsDisplayForKey:(NSString *)key
{
    return [key isEqualToString:@"myProgressKey"];
}

Am I missing anything? Or have you already had this kind of bug with NSOutlineView/NSTableView?

Thanks a lot!

Micha Mazaheri
  • 3,481
  • 1
  • 21
  • 26
  • 1
    1. When a row is deleted, the rowView is cached, so you need to implement a delegate method to discard the associated CALayer. 2. When a cell or row is resized, you probably need to resize the CALayer... where is the code for that? – stevesliva Sep 15 '14 at 16:06
  • Thanks a lot! 1. That's probably a very good point, I'll give it a try. 2. I've tried to set `[self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawDuringViewResize]`, it doesn't change anything, so I don't think it's about resizing the CALayer. What I'm trying right now, is to keep the CALayer only during the animation and remove it when the button is static (drawing the button in the view code). If possible I'd prefer to avoid adding code to the table/outline view related code, and make it a really independent control... – Micha Mazaheri Sep 15 '14 at 17:06
  • Just to keep everybody posted, I made things working by adding the CALayer just before adding the animation, and removing it right after it. The result is visually very clean, and resize, deletion or any view changes are neat (as it's NSView-based drawing code), while animation is smooth as it's CALayer-based. Also, I noticed that the behavior with NSTableView & NSOutlineView is very different. It sounds like (as @stevesliva suggests) the way the framework caches rows is different in both implementations. Anyways, I prefer having my solution and keep my table/outline view code away from that. – Micha Mazaheri Sep 15 '14 at 17:28

0 Answers0