A custom view has some CALayers as its content.
I want to add a consistent rotation animation for the red line. (imagine it as pointer of an analog clock)
Expected result: the red line rotates at the center of the blue square.
Current result: the red line rotates at the left bottom corner of the blue square. See gif below.
The animation code for the red line is :
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
// Set animation properties
rotationAnimation.fromValue = CGFloat.pi * 2.0
rotationAnimation.toValue = 0
rotationAnimation.duration = 2.0
rotationAnimation.isCumulative = true
rotationAnimation.repeatCount = Float.greatestFiniteMagnitude
// Add rotation animation to pointer layer
pointerLayer.add(rotationAnimation, forKey: "rotationAnimation")
The complete project is here:
https://github.com/cool8jay/public/files/11426695/testDrawPointer.zip
Here are what I have tried after google or stackoverflow online:
None works for me.
You forgot to set the frame of your layers, add these lines to your code and it will works:
borderLayer.frame = bounds
pointerLayer.frame = borderLayer.bounds
Leo's answer works, too. The reason you are seeing the red pointer stretch out the blue border is because the color and shape you use, combine with the stroke line's parallel drawing and bounds clipping, can easily create optical illusion. So it would looks like its not rotating around the center but in fact it do.
Here are the complete code:
class ClockView: NSView {
let pointerLayer = CAShapeLayer()
let borderLayer = CAShapeLayer()
override public init(frame frameRect: NSRect) {
super.init(frame: frameRect)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
self.wantsLayer = true
borderLayer.frame = bounds
borderLayer.fillColor = NSColor.gray.cgColor
borderLayer.path = CGPath(ellipseIn: borderLayer.bounds, transform: nil)
layer?.addSublayer(borderLayer)
let path = CGMutablePath()
path.move(to: CGPointMake(bounds.midX, bounds.midY))
path.addLine(to: CGPointMake(bounds.midX + 50, bounds.midY))
pointerLayer.path = path
pointerLayer.frame = bounds
pointerLayer.lineWidth = 8
pointerLayer.strokeColor = NSColor.red.cgColor
layer?.addSublayer(pointerLayer)
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = CGFloat.pi * 2
animation.toValue = 0
animation.duration = 8
animation.isCumulative = true
animation.repeatCount = Float.greatestFiniteMagnitude
pointerLayer.add(animation, forKey: "rotationAnimation")
}
}
I change the borderLayer's shape to a circle, get rid of the line stroke, as well as changing the color to a least dazzling one.
I also rearrange the code a bit so its easier to read.