Black Feather
Black Feather

Reputation: 31

macOS Swift CALayer.contents does not load image

The code below creates a red rectangle that is animated to move across the view from left to right. I would like to have an arbitrary shape loaded from an image to either superimpose or replace the rectangle. However, the circleLayer.contents = NSImage statement in the initializeCircleLayer function doesn't produce any effect. The diagnostic print statement seems to verify that the image exists and has been found, but no image appears in the view. How do I get an image into the layer to replace the animated red rectangle? Thanks!

CODE BELOW:

import Cocoa

class ViewController: NSViewController {

   var circleLayer = CALayer()

   override func viewDidLoad() {

       super.viewDidLoad()
       self.view.wantsLayer = true
       initializeCircleLayer()
       simpleCAAnimationDemo()
   }

   func initializeCircleLayer(){

       circleLayer.bounds = CGRect(x: 0, y: 0, width: 150, height: 150)
       circleLayer.position = CGPoint(x: 50, y: 150)
       circleLayer.backgroundColor = NSColor.red.cgColor
       circleLayer.cornerRadius = 10.0

       let testIm = NSImage(named: NSImage.Name(rawValue: "testImage"))
       print("testIm = \(String(describing: testIm))")

       circleLayer.contents = NSImage(named: NSImage.Name(rawValue:  "testImage"))?.cgImage
       circleLayer.contentsGravity = kCAGravityCenter

       self.view.layer?.addSublayer(circleLayer)
   }

   func simpleCAAnimationDemo(){

       circleLayer.removeAllAnimations()
       let animation = CABasicAnimation(keyPath: "position")
       let startingPoint = NSValue(point: NSPoint(x: 50, y: 150))
       let endingPoint = NSValue(point: NSPoint(x: 600, y: 150))
       animation.fromValue = startingPoint
       animation.toValue = endingPoint
       animation.repeatCount = Float.greatestFiniteMagnitude
       animation.duration = 10.0
       circleLayer.add(animation, forKey: "linearMovement")
   }

}

Upvotes: 1

Views: 557

Answers (1)

David Rönnqvist
David Rönnqvist

Reputation: 56625

Why it doesn't work

The reason why

circleLayer.contents = NSImage(named: NSImage.Name(rawValue:  "testImage"))?.cgImage

doesn't work is because it's a reference to the cgImage(forProposedRect:context:hints:) method, meaning that its type is

((UnsafeMutablePointer<NSRect>?, NSGraphicsContext?, [NSImageRep.HintKey : Any]?) -> CGImage?)? 

You can see this by assigning NSImage(named: NSImage.Name(rawValue: "testImage"))?.cgImage to a local variable and -clicking it to see its type.

The compiler allows this assignment because circleLayer.contents is an Any? property, so literally anything can be assigned to it.

How to fix it

As of macOS 10.6, you can assign NSImage objects to a layers contents directly:

circleLayer.contents = NSImage(named: NSImage.Name(rawValue:  "testImage"))

Upvotes: 1

Related Questions