'Unable to animate a Swift custom property with animator() [ OSX ]

I am trying to animate a custom property using animator() in swift for Objective-C. But I am getting a EXC_BAD_ACCESS exception when I try and set the property through the animator. In a previous application, written in Objective-C, I had used the same idea and it worked.

Here is the class in Swift:

import Cocoa
import QuartzCore


@IBDesignable
class StopPlayButton: NSView {

    //This is the property that I am trying to animate
    @IBInspectable
    var playButtonShapeInterval:Float = 1.0 {
        didSet{
            self.needsDisplay = true
        }
    }

    @IBInspectable
    var isPressed:Bool = false{
        didSet{
            self.needsDisplay = true
        }
    }
    override func drawRect(dirtyRect: NSRect) {
        super.drawRect(dirtyRect)
        StyleKit.drawPlayStopButton(playButtonShapeInterval: CGFloat(self.playButtonShapeInterval), buttonPressed: self.isPressed)
    }

    func toggle(){
        NSAnimationContext.beginGrouping()
        NSAnimationContext.currentContext().duration = 1.0;
        NSAnimationContext.currentContext().timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut);
        if self.playButtonShapeInterval == 0 {
            self.animator().playButtonShapeInterval = 1.0
        }
        else{
            self.animator().playButtonShapeInterval = 0.0//exception happending here...
        }
        NSAnimationContext.endGrouping()
    }
    override static func defaultAnimationForKey(key: String) -> AnyObject?{
        if key == "playButtonShapeInterval" {
            return CABasicAnimation()
        }
        return super.defaultAnimationForKey(key)
    }

}

Note, if I change the property name to playButtonShapeInterval to alphaValue, then the alpha animation works.

Below is the above code ported to Objective-C, doing what I want. I am not sure what is different:

//StopPlayButton.m
#import "StopPlayButton.h"
#import "StyleKit.h"
#import <QuartzCore/QuartzCore.h>

@implementation StopPlayButton

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    NSLog(@"This is the interval: %f", self.playButtonShapeInterval);
    [StyleKit drawPlayStopButtonWithPlayButtonShapeInterval:self.playButtonShapeInterval buttonPressed:self.playButtonPressed];
}

-(void)setPlayButtonShapeInterval:(float)playButtonShapeInterval{
    if(playButtonShapeInterval != _playButtonShapeInterval){
        _playButtonShapeInterval = playButtonShapeInterval;
        [self setNeedsDisplay:YES];
    }
}

-(void)toggle{
    [NSAnimationContext beginGrouping];
    [[NSAnimationContext currentContext] setDuration:0.5];
    [[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    if(self.playButtonShapeInterval == 0){
        self.animator.playButtonShapeInterval = 1;
    }
    else{
        self.animator.playButtonShapeInterval = 0;
    }
    [NSAnimationContext endGrouping];
}

+(id)defaultAnimationForKey:(NSString *)key{
    if ([key isEqualToString:@"playButtonShapeInterval"]) {
        return [CABasicAnimation animation];
    }
    return [super defaultAnimationForKey:key];
}

@end

//StopPlayButton.h
#import <Cocoa/Cocoa.h>

@interface StopPlayButton : NSView

@property (nonatomic) BOOL playButtonPressed;
@property (nonatomic) float playButtonShapeInterval;

-(void)toggle;

@end


Solution 1:[1]

I met the same problem and fixed it by declaring the custom property as 'dynamic'. I believe it's required by KVO.

dynamic var playButtonShapeInterval:Float = 1.0 {
    didSet{
        self.needsDisplay = true
    }
}

Solution 2:[2]

In Swift 5, you need this:

@objc class YourView: NSClipView {
    @objc dynamic var yourAnimatableProperty = CGFloat(1)

    override static func defaultAnimation(forKey key: NSAnimatablePropertyKey) -> Any? {
        if key == "yourAnimatableProperty" {
            return CABasicAnimation()
        }
        return super.defaultAnimation(forKey: key)
    }

    ...
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Wevah