UIImageView is not animating - ios

I am trying to have a view in which 3 dots would animate infinity and here is the code snippet(swift 5) for it
func showAnimatingDotsInImageView() {
let lay = CAReplicatorLayer()
lay.frame = CGRect(x: 0, y: 0, width: 15, height: 7) //yPos == 12
let circle = CALayer()
circle.frame = CGRect(x: 0, y: 0, width: 7, height: 7)
circle.cornerRadius = circle.frame.width / 2
circle.backgroundColor = UIColor.green.cgColor
lay.addSublayer(circle)
lay.instanceCount = 3
lay.instanceTransform = CATransform3DMakeTranslation(10, 0, 0)
let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
anim.fromValue = 1.0
anim.toValue = 0.2
anim.duration = 1
anim.repeatCount = .infinity
circle.add(anim, forKey: nil)
lay.instanceDelay = anim.duration / Double(lay.instanceCount)
test1.layer.addSublayer(lay)
}
In the above code test1 is a UIImageView.
The main issue is the dots are shown but it's static and doesn't animate.

I tried your code and it is animating just fine.
Any chance you are calling this from a thread which is different than the Main thread? Like for example something on the background is happening, which you want to triggers the loading?
Try adding:
DispatchQueue.main.async { [weak self] in
self?.showAnimatingDotsInImageView()
}

Related

ReplicationLayer stopping animation once the first instance finishes animating, but the other instance animations are cut short

I have attached a gif of what the animation looks like when setup as described below. How can I ensure that all 6 instances of the replicatorLayer finish animating and then remove the leftover coin image (i feel using dispatchQueue.asyncAfter is insufficient for animationPurposes, but am unaware of another solution).
I have a CAReplicationLayer as the first and only subLayer of my views primary CALayer object.
let replicatorLayer = CAReplicatorLayer()
replicatorLayer.instanceDelay = TimeInterval(0.1)
replicatorLayer.instanceCount = 6
replicatorLayer.frame.size = frame.size
layer.addSublayer(replicatorLayer)
I create another CALayer for an image and some text and add it as a sublayer replicatorLayer
let coinWithTextLayer = CALayer()
coinWithTextLayer.frame = CGRect(x: circleCenter.x, y: circleCenter.y, width: 61, height: 67)
coinWithTextLayer.masksToBounds = false
let imageLayer = CALayer()
imageLayer.contents = image.cgImage
imageLayer.frame = CGRect(x: 0, y: 20, width: 49, height: 49)
imageLayer.contentsGravity = .resizeAspect
let textLayer = CATextLayer()
textLayer.frame = CGRect(x: 34, y: 0, width: 40, height: 19)
textLayer.fontSize = 20
textLayer.string = "+ \(5)"
coinWithTextLayer.addSublayer(imageLayer)
coinWithTextLayer.addSublayer(textLayer)
replicatorLayer.addSublayer(coinWithTextLayer)
I then apply an animation group to the coinWithTextLayer that animates it up and to the right, while also scaling from 0.1 to 1
let positionAnim1 = CABasicAnimation(keyPath: "position")
positionAnim1.fromValue = circleCenter
positionAnim1.toValue = CGPoint(x: circleCenter.x + 24, y: circleCenter.y - 24)
let scaleAnim1 = CABasicAnimation(keyPath: "transform.scale")
scaleAnim1.fromValue = 0.1
scaleAnim1.toValue = 1
scaleAnim1.delegate = self
let firstAnimationGroup = CAAnimationGroup()
firstAnimationGroup.beginTime = 0
firstAnimationGroup.duration = 2
firstAnimationGroup.autoreverses = false
firstAnimationGroup.animations = [positionAnim1, scaleAnim1]
firstAnimationGroup.delegate = self
firstAnimationGroup.fillMode = .backwards
coinWithTextLayer.add(firstAnimationGroup, forKey: "tapCoinAnim1")
/*DispatchQueue.main.asyncAfter(seconds: 0.5 + (0.1 * 6)) { // 0.1 * 6 to account for the delay between instances of the replicatorLayer
coinWithTextLayer.removeFromSuperlayer()
coinWithTextLayer.removeAllAnimations()
}*/ // Commented out because it does successfuly remove the coin after the animation, but feels like an incorrect/inefficient way of doing so

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?

CABasicAnimation not scaling around center

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.

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

Add animation to UIImageView

I am trying to animate an imageView with this CAAnimation so I added the following extension to UIImageView:
extension UIImageView {
var ovalPathSmall: UIBezierPath {
return UIBezierPath(ovalInRect: CGRect(x: 50.0, y: 50.0, width: 0.0, height: 0.0))
}
var ovalPathLarge: UIBezierPath {
return UIBezierPath(ovalInRect: CGRect(x: 2.5, y: 17.5, width: 95.0, height: 95.0))
}
var ovalPathSquishVertical: UIBezierPath {
return UIBezierPath(ovalInRect: CGRect(x: 2.5, y: 20.0, width: 95.0, height: 90.0))
}
var ovalPathSquishHorizontal: UIBezierPath {
return UIBezierPath(ovalInRect: CGRect(x: 5.0, y: 20.0, width: 90.0, height: 90.0))
}
func wobble() {
let animationDuration: CFTimeInterval = 0.3
// 1
var wobbleAnimation1: CABasicAnimation = CABasicAnimation(keyPath: "path")
wobbleAnimation1.fromValue = ovalPathLarge.CGPath
wobbleAnimation1.toValue = ovalPathSquishVertical.CGPath
wobbleAnimation1.beginTime = 0.0
wobbleAnimation1.duration = animationDuration
// 2
var wobbleAnimation2: CABasicAnimation = CABasicAnimation(keyPath: "path")
wobbleAnimation2.fromValue = ovalPathSquishVertical.CGPath
wobbleAnimation2.toValue = ovalPathSquishHorizontal.CGPath
wobbleAnimation2.beginTime = wobbleAnimation1.beginTime + wobbleAnimation1.duration
wobbleAnimation2.duration = animationDuration
// 3
var wobbleAnimation3: CABasicAnimation = CABasicAnimation(keyPath: "path")
wobbleAnimation3.fromValue = ovalPathSquishHorizontal.CGPath
wobbleAnimation3.toValue = ovalPathSquishVertical.CGPath
wobbleAnimation3.beginTime = wobbleAnimation2.beginTime + wobbleAnimation2.duration
wobbleAnimation3.duration = animationDuration
// 4
var wobbleAnimation4: CABasicAnimation = CABasicAnimation(keyPath: "path")
wobbleAnimation4.fromValue = ovalPathSquishVertical.CGPath
wobbleAnimation4.toValue = ovalPathLarge.CGPath
wobbleAnimation4.beginTime = wobbleAnimation3.beginTime + wobbleAnimation3.duration
wobbleAnimation4.duration = animationDuration
// 5
var wobbleAnimationGroup: CAAnimationGroup = CAAnimationGroup()
wobbleAnimationGroup.animations = [wobbleAnimation1, wobbleAnimation2, wobbleAnimation3,
wobbleAnimation4]
wobbleAnimationGroup.duration = wobbleAnimation4.beginTime + wobbleAnimation4.duration
wobbleAnimationGroup.repeatCount = 2
self.layer.addAnimation(wobbleAnimationGroup, forKey: nil)
}
then this to my viewDiLoad:
NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "x",
userInfo: nil, repeats: false)
func x() {
questionIcon.wobble()
}
questionIcon is an outlet to the image I want to animate, but it doesn't move! What is wrong with the code? thanks
It isn't at all clear what you are trying to do; your code as it stands is nonsense. You are making animation objects like this:
CABasicAnimation(keyPath: "path")
But a UIImageView does not have a path property so you are not animating anything. The whole notion that a UIImageView might have an animatable path property is just something you made up in your head. You don't get to do that...
What you are allowed to do with a UIImageView is prepare a sequence of images (which you can create in code) and hand them to the image view to be played in sequence (as its animationImages) like the frames of a cartoon. Perhaps that is the direction you should be taking here.

Resources