NSValue valueWithCATransform3D: in Swift3 - ios

How to translate the following animation into Swift3?
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.5, 1)];
What I currently have is
let circleEnlargeAnimation = CABasicAnimation(keyPath: "transform.scale")
circleEnlargeAnimation.fromValue = CATransform3DIdentity
circleEnlargeAnimation.toValue = CATransform3DMakeScale(10.0, 10.0, 1.0)
But my animation is not working... The following is the full animation code:
let shapeLayer = CAShapeLayer()
let center = CGPoint(x: sampleButton.bounds.width/2.0, y: sampleButton.bounds.height/2.0)
let radius = CGFloat(10.0)
let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0.0, endAngle: (CGFloat(360.0 * M_PI) / 180.0), clockwise: true)
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.white.cgColor
shapeLayer.lineCap = kCALineCapRound
shapeLayer.frame = sampleButton.bounds
let circleEnlargeAnimation = CABasicAnimation(keyPath: "transform.scale")
circleEnlargeAnimation.fromValue = CATransform3DIdentity
circleEnlargeAnimation.toValue = CATransform3DMakeScale(10.0, 10.0, 1.0)
circleEnlargeAnimation.duration = 1.0
shapeLayer.add(circleEnlargeAnimation, forKey: nil)
sampleButton.layer.addSublayer(shapeLayer)
It only shows a circle but is not animating...

You should either use the key transform.scale with a scalar numeric value or transform with a matrix value. See Key Value Coding Extensions in Core Animation Programming Guide for a full list of available keys.
You can create a matrix value for instance with:
NSValue(caTransform3D:CATransform3DMakeScale(10.0, 10.0, 1.0))

Related

Center of CAGradientLayer returns wrong position on rotate animation

I have a circle like in Instagram profile image which has story. I want to have an affect like circle is spinning. For that, I used CABasicAnimation. It is spinning but not in center.
As I searched, I need to give bounty for shapeLayer but when I do that, It doesn't placed where It needs to be.
How can I achieve animation like in Instagram story circle (like circle is spinning)?
EDIT I also try to add "colors" animation but because It works like It is in square, I can't get the desired result.
func addCircle() {
let circularPath = UIBezierPath(arcCenter: CGPoint(x: self.bounds.midX, y: self.bounds.midY), radius: self.bounds.width / 2, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 10
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = kCALineCapRound
shapeLayer.strokeEnd = 1.0
gradient.frame = circularPath.bounds
gradient.colors = [UIColor.blue.cgColor, UIColor.red.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
shapeLayer.path = circularPath.cgPath
gradient.mask = shapeLayer
self.layer.addSublayer(gradient)
}
let rotationAnimation: CAAnimation = {
let animation = CABasicAnimation(keyPath: "transform.rotation")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 4
animation.repeatCount = MAXFLOAT
return animation
}()
#objc private func handleTap() {
print("Attempting to animate stroke")
shapeLayer.add(rotationAnimation, forKey: "urSoBasic2")
}
If spinning, why not spin gradient
let rotationAnimation: CAAnimation = {
let animation = CABasicAnimation(keyPath: "transform.rotation")
animation.fromValue = 0
animation.toValue = Float.pi * 2
animation.duration = 4
animation.repeatCount = MAXFLOAT
return animation
}()
#objc func handleTap() {
print("Attempting to animate stroke")
gradient.add(rotationAnimation, forKey: "urSoBasic2")
}
As to off center problem. The correct frame for gradient should be like this :
gradient.frame = self.bounds
To verify the center, you may add a background for the view:
self.background = UIColor.black.
The reason is width and height of the view is not set well due to the constraints. Try to set the correct bounds of the view, so when you add the circularPath, it's center in the view.

Unable to get correct center for CAShapeLayer

Can someone please point out what am i doing incorrectly? I need to adapt to all iphone screens in portrait. is this a good approach?
var progressCircle = CAShapeLayer()
let centerPoint = CGPoint (x: vu_SubmissionViewWithImageInCenter.bounds.width / 2, y: imgVu_Submission.bounds.width / 2)
let circleRadius : CGFloat = vu_SubmissionViewWithImageInCenter.bounds.width / 2 * 0.94
let circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true )
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath.cgPath
progressCircle.strokeColor = UIColor.fuschia.cgColor
progressCircle.fillColor = UIColor.clear.cgColor
progressCircle.lineWidth = 3.5
progressCircle.strokeStart = 0
progressCircle.strokeEnd = 1.0
vu_SubmissionViewWithImageInCenter.layer.addSublayer(progressCircle)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1.0
animation.duration = 5.0
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
progressCircle.add(animation, forKey: "ani")
animation.delegate = self
5s
Your radius calculation is wrong. Have a distance calculation from arc centre to the imageview edges and use that as your radius.

animating path in swift 3

following code creates a red 270 degree ring
let result = Measurement(value: 270, unit: UnitAngle.degrees)
.converted(to: .radians).value
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 200,y: 200), radius: CGFloat(90), startAngle: CGFloat(0), endAngle:CGFloat(result), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
//change the fill color
shapeLayer.fillColor = UIColor.clear.cgColor
//you can change the stroke color
shapeLayer.strokeColor = UIColor.red.cgColor
//you can change the line width
shapeLayer.lineWidth = 30.0
view.layer.addSublayer(shapeLayer)
How would i go about and animate the path being drawn using swift 3
thanks
CAShapeLayer has properties strokeStart and strokeEnd that define what part of the path you want to draw. Their default values are 0.0 and 1.0 respectively, since you usually want to draw the entire path.
You can animate the change of these values using a CABasicAnimation by specifying the "strokeEnd" or "strokeStart" value key, like this:
let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
pathAnimation.fromValue = 0.0
pathAnimation.toValue = 1.0
pathAnimation.duration = 1.0 // time in seconds.
shapeLayer.add(pathAnimation, forKey: "strokeEnd")

Circular progress in swift

I am trying to make a simple circular progress bar in swift. What I have failed to do so far is, the progress should start at the top of the circle (at the moment it is starting at the 90 degree point). I would also like to have a circular line under the progress line that should be thicker than the progress line and have a different color as well..
Can anyone put me in the right direction towards my wishes?
Here is my function:
func animateView() {
let circle = viewProgress // viewProgress is a UIView
var progressCircle = CAShapeLayer()
let circlePath = UIBezierPath(ovalInRect: circle.bounds)
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath.CGPath
progressCircle.strokeColor = UIColor.greenColor().CGColor
progressCircle.fillColor = UIColor.clearColor().CGColor
progressCircle.lineWidth = 5.0
circle.layer.addSublayer(progressCircle)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1.5
animation.duration = 1
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
progressCircle.addAnimation(animation, forKey: "ani")
}
Igor this is how it looks:
For Swift 4
Declare two CAShapeLayers. One for actual circle and another for progress Layer!
func createCircularPath() {
let circleLayer = CAShapeLayer()
let progressLayer = CAShapeLayer()
let center = self.view.center
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -.pi / 2, endAngle: 2 * .pi, clockwise: true)
circleLayer.path = circularPath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.lightGray.cgColor
circleLayer.lineCap = .round
circleLayer.lineWidth = 20.0 //for thicker circle compared to progressLayer
progressLayer.path = circularPath.cgPath
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeColor = UIColor.blue.cgColor
progressLayer.lineCap = .round
progressLayer.lineWidth = 10.0
progressLayer.strokeEnd = 0
view.addSublayer(circleLayer)
view.addSublayer(progressLayer)
let progressAnimation = CABasicAnimation(keyPath: "strokeEnd")
progressAnimation.toValue = 1
progressAnimation.fillMode = .forwards
progressAnimation.isRemovedOnCompletion = false
progressLayer.add(progressAnimation, forKey: "progressAnim")
}
Happy Coding!
Hope this answer helps :]

Animating circular progress View in Swift

I want to have an animation for my circular progressView with the code below. I want the animation start the progress from 0 to the value I want.
But I can never get the animation work.
func Popular(){
let circle = Pop;//Pop is a UIView
circle.bounds = CGRect(x: 0,y: 0, width: 100, height: 100);
circle.frame = CGRectMake(0,0, 100, 100);
circle.layoutIfNeeded()
var progressCircle = CAShapeLayer();
let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2);
let circleRadius : CGFloat = circle.bounds.width / 2 * 0.83;
let circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true );
progressCircle = CAShapeLayer ();
progressCircle.path = circlePath.CGPath;
progressCircle.strokeColor = UIColor.greenColor().CGColor;
progressCircle.fillColor = UIColor.clearColor().CGColor;
UIView.animateWithDuration(3.0,
delay: 1.0,
options: [],
animations: {
progressCircle.lineWidth = 5.0;
progressCircle.strokeStart = 0;
progressCircle.strokeEnd = 0.20;
circle.layer.addSublayer(progressCircle);
circle
progressCircle.strokeEnd = 0.2;
},
completion: { finished in
print("Picutre done")
})
}
Use CABasicAnimation
let circle = Pop
var progressCircle = CAShapeLayer()
let circlePath = UIBezierPath(ovalInRect: circle.bounds)
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath.CGPath
progressCircle.strokeColor = UIColor.greenColor().CGColor
progressCircle.fillColor = UIColor.clearColor().CGColor
progressCircle.lineWidth = 5.0
circle.layer.addSublayer(progressCircle)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 0.2
animation.duration = 3
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
progressCircle.addAnimation(animation, forKey: "ani")
Two things -
First - Do not re-initialize progressCircle. You are calling progressCircle = CAShapeLayer (); twice. This is not bug here though.
Second - Add your sublayer before you start animating it. Move circle.layer.addSublayer(progressCircle); outside the animation block.

Resources