macoscocoansbuttonnsanimationcontextnsanimation

NSAnimation removes button background colour


I am working on a Mac app. I am trying to do a simple animation which makes an NSButton move down. The animation works really nicely, but when i do it, the background colour of my NSButton disappears for some reason. Here is my code:

// Tell the view to create a backing layer.
additionButton.wantsLayer = YES;

// Set the layer redraw policy. This would be better done in
// the initialization method of a NSView subclass instead of here.
additionButton.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;

[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
    context.duration = 1.0f;
    additionButton.animator.frame = CGRectOffset(additionButton.frame, 0.0, -20.0);
    //additionButton.frame = CGRectOffset(additionButton.frame, 0.0, -20.0);
} completionHandler:nil];

Button move down animation:

enter image description here

Button after move down animation:

enter image description here

Update 1

Just to make it clear, I am not using a background image in my buttons. I am using a background NSColor which I set in the viewDidLoad method like so:

[[additionButton cell] setBackgroundColor:[NSColor colorWithRed:(100/255.0) green:(43/255.0) blue:(22/255.0) alpha:1.0]];

Solution

  • I presume this is an AppKit bug. There are a couple of ways you can work around it.


    Workaround 1:

    Don't use layers. The button you're animating seems to be small, and you might be able to get away with using a non layer-backed animation and still have it look decent. The button will redraw during each step of the animation, but it will animate correctly. That means this is really all you have to do:

    [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {          
        additionButton.animator.frame = CGRectOffset(additionButton.frame, 0, -20);
    } completionHandler:nil];
    

    Workaround 2:

    Set the background color on the layer.

    additionButton.wantsLayer = YES;
    additionButton.layer.backgroundColor = NSColor.redColor.CGColor;
    additionButton.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
    
    [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {          
        additionButton.animator.frame = CGRectOffset(additionButton.frame, 0, -20);
    } completionHandler:nil];
    

    Workaround 3:

    Subclass NSButtonCell, and implement -drawBezelWithFrame:inView:, drawing your background color there. Keep in mind that the parent view containing the button should be layer-backed, otherwise the button will still redraw on every step.