CABasicAnimation not scaling around center - ios

I want to perform opacity And Scale effect at same time my animation work perfect but it's position is not proper. i want to perform animation on center.
This is my code.
btn.backgroundColor = UIColor.yellowColor()
let stroke = UIColor(red:236.0/255, green:0.0/255, blue:140.0/255, alpha:0.8)
let pathFrame = CGRectMake(24, 13, btn.bounds.size.height/2, btn.bounds.size.height/2)
let circleShape1 = CAShapeLayer()
circleShape1.path = UIBezierPath(roundedRect: pathFrame, cornerRadius: btn.bounds.size.height/2).CGPath
circleShape1.position = CGPoint(x: 2, y: 2)
circleShape1.fillColor = stroke.CGColor
circleShape1.opacity = 0
btn.layer.addSublayer(circleShape1)
circleShape1.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = NSValue(CATransform3D: CATransform3DIdentity)
scaleAnimation.toValue = NSValue(CATransform3D: CATransform3DMakeScale(2.0, 2.0, 1))
let alphaAnimation = CABasicAnimation(keyPath: "opacity")
alphaAnimation.fromValue = 1
alphaAnimation.toValue = 0
CATransaction.begin()
let animation = CAAnimationGroup()
animation.animations = [scaleAnimation, alphaAnimation]
animation.duration = 1.5
animation.repeatCount = .infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
circleShape1.addAnimation(animation, forKey:"Ripple")
CATransaction.commit()

The problem is that you are not grappling with frames correctly. You say:
let circleShape1 = CAShapeLayer()
But you have forgotten to give circleShape1 a frame! Thus, its size is zero, and very weird things happen when you animate it. Your job in the very next line should be to assign circleShape1 a frame. Example:
circleShape1.frame = pathFrame
That may or may not be the correct frame; it probably isn't. But you need to figure that out.
Then, you need to fix the frame of your Bezier path in terms of the shape layer's bounds:
circleShape1.path = UIBezierPath(roundedRect: circleShape1.bounds // ...

I have never worked with sublayers so I would make it with a subview instead, makes the code a lot easier:
btn.backgroundColor = UIColor.yellow
let circleShape1 = UIView()
circleShape1.frame.size = CGSize(width: btn.frame.height / 2, height: btn.frame.height / 2)
circleShape1.center = CGPoint(x: btn.frame.width / 2, y: btn.frame.height / 2)
circleShape1.layer.cornerRadius = btn.frame.height / 4
circleShape1.backgroundColor = UIColor(red:236.0/255, green:0.0/255, blue:140.0/255, alpha:0.8)
circleShape1.alpha = 1
btn.addSubview(circleShape1)
UIView.animate(withDuration: 1,
delay: 0,
options: [.repeat, .curveLinear],
animations: {
circleShape1.transform = CGAffineTransform(scaleX: 5, y: 5)
circleShape1.alpha = 0.4
}, completion: nil)

You need to create path frame with {0,0} position, and set correct frame to the Layer:
let pathFrame = CGRectMake(0, 0, btn.bounds.size.height/2, btn.bounds.size.height/2)
....
circleShape1.frame = CGRect(x: 0, y: 0, width: pathFrame.width, height: pathFrame.height)
circleShape1.position = CGPoint(x: 2, y: 2)
If you want to create path with {13,24} position you need to change width and height in layer. Your shape should be in center of layer.

Related

What is the proper way to scale a view while animating it with CAKeyframeAnimation and BezierPath

The following code effectively animates and scales a view along a BezierPath but I feel like I'm cheating since I'm mixing UIKit and CAKeyframeAnimation (Core Animation) animations.
Is there a better way to scale a view while animating it along the BezierPath?
func animateIt(){
let myView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
myView.backgroundColor = .blue
myView.layer.cornerRadius = myView.frame.height / 2
view.addSubview(myView)
let startPoint = CGPoint(x: 250, y: 500)
let apexPoint = CGPoint(x: 100, y: 50)
let endPoint = CGPoint(x: 50, y: 350)
let durationTime = 1.5
let path = UIBezierPath()
path.move(to: startPoint)
path.addQuadCurve(to: endPoint, controlPoint: apexPoint)
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = path.cgPath
animation.duration = durationTime
myView.layer.add(animation, forKey: "bezier")
myView.center = endPoint
UIView.animate(withDuration: durationTime, animations: {
myView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5);
})
}
I tried using a second CAKeyframeAnimation animation instead of the UIKit animation and used the array of key values but no matter what I do, I cannot get a uniform scale, it scales at the beginning but not at the end.
let scaleAnimation = CAKeyframeAnimation(keyPath: "scale")
scaleAnimation.values = [1.0, 0.9, 0.5,]
myView.layer.add(scaleAnimation, forKey: "scale")
Thanks!

Swift - CABasicAnimation not working with CALayer

Here is my code about add a layer with animation added.
let myLayer: CALayer = .init()
let myAnimation: CABasicAnimation = .init(keyPath: "bounds.size.height")
// for myLayer to center of superview
let centerPoint: CGPoint = .init(x: mySuperview.bounds.width / 2, y: mySuperview.bounds.height / 2)
myLayer.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
myLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
myLayer.position = centerPoint
myLayer.backgroundColor = UIColor.red.cgColor
myAnimation.fromValue = 20
myAnimation.toValue = 100
myAnimation.duration = 1
myAnimation.beginTime = CACurrentMediaTime() + 1.0
myAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
myLayer.add(myAnimation, forKey: "bounds.size.height")
mySuperview.layer.addSublayer(myLayer)
and myLayer was appeared at correct position. but added animation isn't working.
not only about bounds.size.height. frame.size.height, transform, opacity... I tried all of them but It isn't changing.
I am confused because I have a recollection of working well easily by above code.
Did I skip anything?

UIView fill animation in iOS Swift?

I want to fill the color of view from left to right as fill animation. I have been using this code to get the effect but somehow it doesn't show any animation.i am using below code.
UIView.animate(withDuration: 2.0, delay: 0.2, options:.curveLinear, animations: {
view!.backgroundColor = Constant.AppColor.viewBottomFocus
}, completion:nil)
please tell me how can i achieve this?
I don't think you can fill a color with left to right animation with simple UIView blocks. You can ofcourse change width or trailing constraint of the view and it will appear that the view is changing its color. But, I dont think thats the ideal way to do it.
To achieve what you want I guess you have to use gradient layer and animate the locations:
class AnimatingV:UIView {
func animate() {
let layer = CAGradientLayer()
let startLocations = [0, 0]
let endLocations = [1, 2]
layer.colors = [UIColor.red.cgColor, UIColor.white.cgColor]
layer.frame = self.frame
layer.locations = startLocations as [NSNumber]
layer.startPoint = CGPoint(x: 0.0, y: 1.0)
layer.endPoint = CGPoint(x: 1.0, y: 1.0)
self.layer.addSublayer(layer)
let anim = CABasicAnimation(keyPath: "locations")
anim.fromValue = startLocations
anim.toValue = endLocations
anim.duration = 2.0
layer.add(anim, forKey: "loc")
layer.locations = endLocations as [NSNumber]
}
}
let animatingV = AnimatingV(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
animatingV.backgroundColor = UIColor.yellow
animatingV.animate()
PlaygroundPage.current.liveView = animatingV
Create UIView in storyboard and make its subclass of UIProgressView
then use this code to fill view , you can use animated fill as well by calling this function in scheduler
//progressView is view that you have create in storyboard
progressView.progress = .25; // .25 is the fill rate of total view
progressView.trackTintColor = [UIColor whiteColor];
progressView.progressTintColor = [UIColor redColor];

Transform doesn't work with animation

I'm trying to set up a CALayer to increase in width at the bottom of my viewcontroller by using CATransform3DMakeScale. I can get the layer to scale just fine, but when I try to apply the transformation through an animation, the layer transforms without any animation.
let progressBar1 = CALayer()
override func viewDidAppear() {
progressBar1.bounds = CGRect(x: 0, y: 0, width: 1, height: 5)
progressBar1.position = CGPoint(x: 0, y: 600)
progressBar1.backgroundColor = UIColor.white.cgColor
view.layer.addSublayer(progressBar1)
extendBar1()
}
func extendBar1(){
let transform1 = CATransform3DMakeScale(30, 1, 0)
let anim = CABasicAnimation(keyPath: "transform")
anim.isRemovedOnCompletion = false
anim.fillMode = kCAFillModeForwards
anim.toValue = NSValue(caTransform3D:transform1)
anim.duration = 10.00
progressBar1.add(anim, forKey: "transform")
}
I also tried the following with CATransaction but I get the same result
func extendBar3(){
let transform1 = CATransform3DMakeScale(30, 1, 0)
CATransaction.begin()
CATransaction.setAnimationDuration(7.0)
progressBar1.transform = transform1
CATransaction.commit()
}
The chief remaining problem is this line:
let transform1 = CATransform3DMakeScale(30, 1, 0)
Change the 0 to a 1.
(The result may still not be the animation you want, precisely, but at least you should see something — as long as (0,600) is not off the screen entirely, of course.)

CALayer disappears after playing a CABasicAnimation backwards

I have a CALayer and I've added a CABasicAnimation to it like this:
circle = CALayer()
circle.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
circle.backgroundColor = UIColor.green().cgColor
circle.cornerRadius = 50
circle.speed = 0
view.layer.addSublayer(circle)
animation = CABasicAnimation(keyPath: "position")
animation.duration = 1
animation.fromValue = NSValue(cgPoint: CGPoint(x: 50, y: 50))
animation.toValue = NSValue(cgPoint: CGPoint(x: view.bounds.width / 2, y: view.bounds.height / 2))
animation.fillMode = kCAFillModeBoth
animation.isRemovedOnCompletion = false
circle.add(animation, forKey: nil)
When I play the animation backwards by setting the layer's speed to -1,
the layer disappears at the end of the animation:
circle.timeOffset = 1
circle.speed = -1
circle.beginTime = CACurrentMediaTime()
Because of that, I use CADisplayLink to play an animation backwards,
but I'm wondering if it can be achieved by setting the speed to -1 and preventing the layer from disappearing also.

Resources