如何防止 NSImageView 在其父 NSToolbar 为 hidden/shown 时丢失动画?

How to prevent an NSImageView from loosing an animation when its parent NSToolbar is hidden/shown?

Xcode: 9.2.
macOS 目标:10.13

当父 NSToolbar 被隐藏然后随后显示时,NSImageView 似乎会丢失添加到其图层的所有动画。

有没有办法指示 AppKit 成为手 off/restore 动画的状态?

示例代码

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)
    }
}
}

Sample Xcode project on GitHub

通常,当动画层从 on-screen 层树中移除时,动画被视为“完成”。默认情况下,动画完成后会从其图层中移除。 AppKit 从 window 中删除了工具栏视图(以及它的所有子视图及其层),因此动画被认为已完成并从其层中删除。

要保持​​动画安装,您可以将动画的 isRemovedOnCompletion 设置为 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)
    }

}

结果: