How do you animate in rotation a NSStatusItem?

On macOS Sierra the following code worked as expected, showing an animated image, rotating from the "centre" of the view.


        let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
        statusItem.button?.isEnabled = true
        let image = NSImage(named:"statusItem")!
        image.isTemplate = true
        statusItem.button?.image = image
        statusItem.button?.wantsLayer = true
        let basicAnimation = CABasicAnimation(keyPath:"transform.rotation")
        basicAnimation.fromValue = 2.0 * .pi
        basicAnimation.toValue = 0.0
        basicAnimation.duration = 1.0
        basicAnimation.repeatCount = Float.infinity

        statusItem.button?.layer?.add(basicAnimation, forKey: "spinAnimation")

Output


On **macOS High Sierra** however, there is a "background" and the animation happens on the bottom left corner at a wide angle.


Output



AFAICS, customisation of a `NSStatusItem` is pretty limited so decided to jump on Core Animation and layers.

Using the following code


        let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
        statusItem.button?.isEnabled = true
        let layer = CALayer()
        statusItem.button?.layer = layer
        statusItem.button?.wantsLayer = true

        let image = NSImage(named:NSImage.Name(rawValue: "statusItem"))
        image?.isTemplate = true
        statusItem.button?.layer?.contents = image
        let basicAnimation = CABasicAnimation(keyPath:"transform.rotation")
        basicAnimation.fromValue = 2.0 * .pi
        basicAnimation.toValue = 0.0
        basicAnimation.duration = 1.0
        basicAnimation.repeatCount = Float.infinity

        statusItem.button?.layer?.add(basicAnimation, forKey: "spinAnimation")
        statusItem.button?.layer?.anchorPoint = CGPoint(x: 0.5, y: 0.5)


Still leaves the background while the image position initially is at the centre before jumping at the bottom left corner to rotate.


Output


Sample Project

StatusItem


References

Core Animation Basics

Animating Layer Content


Update


Have managed to make the position of the image centred by implementing the `func display(_ layer: CALayer)` method in a `CALayerDelegate`.


        let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
        statusItem.button?.isEnabled = true
        let layer = CALayer()
        layer.delegate = self
        statusItem.button?.layer = layer
        statusItem.button?.wantsLayer = true


        let basicAnimation = CABasicAnimation(keyPath:"transform.rotation")
        basicAnimation.fromValue = 2.0 * .pi
        basicAnimation.toValue = 0.0
        basicAnimation.duration = 1.0
        basicAnimation.repeatCount = Float.infinity

        statusItem.button?.layer?.add(basicAnimation, forKey: "spinAnimation")
        statusItem.button?.layerContentsRedrawPolicy = .onSetNeedsDisplay
        statusItem.button?.layer?.setNeedsDisplay()



CALayerDelegate


    func display(_ layer: CALayer) {
        let frame = layer.frame
        layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        layer.frame = frame
        layer.contentsScale = 2.0
        layer.contentsGravity = "aspectFit"
        let image = NSImage(named:"statusItem")!
        image.isTemplate = true
        layer.contents = image
    }



Still the background remains.

For what is worth, setting the `NSStatusItem. NSStatusBarButton.image` property with the template image displays the image correctly, however there is no way to then animate it.


let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
statusItem.button?.isEnabled = true
let image = NSImage(named:"statusItem")!
image.isTemplate = true
statusItem.button?.image = image



Tried to debug the UI Hierarchy and if I understand this correctly, when setting the NSImage to the `contents` property of the layer, the image is not drawn as a template.


In both cases, the [`backgroundcolor`][11] and [`NSImageRep`][12] are


Generic Gray Gamma 2.2 Profile colorspace 0 0


and


NSCGImageRep 0x6000000850a0 Size={16, 16} ColorSpace=sRGB IEC61966-2.1 colorspace BPS=0 Pixels=32x32 Alpha=NO



respectively.