Xcode: 9.2.
macOS Target: 10.13
It appears that an NSImageView will loose any animations added to its layer when the parent NSToolbar is made hidden then subsequently shown.
Is there a way to instruct AppKit to be hands off/restore the state of the animation?
Example code
class WindowController: NSWindowController, CALayerDelegate {
static let spinAnimation: CAAnimation = {
let basicAnimation = CABasicAnimation(keyPath:"transform.rotation")
basicAnimation.fromValue = 2.0 * .pi
basicAnimation.toValue = 0.0
basicAnimation.duration = 1.0
basicAnimation.repeatCount = Float.infinity
return basicAnimation
}()
@IBOutlet weak var imageView: NSImageView! {
didSet{
let layer = CALayer()
layer.contentsScale = 2.0
layer.contentsGravity = "aspectFit"
layer.contents = #imageLiteral(resourceName: "windmill")
imageView.layer = layer
imageView.wantsLayer = true
imageView.layerContentsRedrawPolicy = .onSetNeedsDisplay
imageView.layer?.delegate = self
imageView.needsDisplay = true
}
}
func display(_ layer: CALayer) {
let frame = layer.frame
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
layer.frame = frame
}
override func windowDidLoad() {
super.windowDidLoad()
let key = "spinAnimation"
self.imageView.layer?.add(WindowController.spinAnimation, forKey: key)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {
self.imageView.layer?.removeAnimation(forKey: key)
}
}
}
Normally, an animation is considered “completed” when its layer is removed from an on-screen layer tree. By default, an animation is removed from its layer when the animation completes. AppKit removes the toolbar view (and hence all its subviews and their layers) from the window, so the animation is considered completed and removed from its layer.
To keep the animation installed, you can set the animation's isRemovedOnCompletion
to false
.
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
@IBOutlet var customItem: NSToolbarItem!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let view = customItem.view!
view.wantsLayer = true
let layer = view.layer!
let frame = layer.frame
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
layer.frame = frame
let animation = CABasicAnimation(keyPath: "transform.rotation")
animation.fromValue = CGFloat(0)
animation.toValue = 2 * CGFloat.pi
animation.duration = 1
animation.repeatCount = .infinity
animation.isRemovedOnCompletion = false
layer.add(animation, forKey: animation.keyPath)
}
}
Result: