please, how to do infinity line animation along path (rounded rectangle) without stopping, the line have to have constant/same length in each time - see attached picture
I found out only this solution, but this isn't what I want.
--- for path:
path = UIBezierPath(roundedRect: CGRect.init(x: 0, y: 0, width: 100, height: 100), byRoundingCorners:.allCorners, cornerRadii: CGSize(width:16, height:16))
--- animations:
let strokeEndAnimation: CAAnimation = {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 10
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
let group = CAAnimationGroup()
group.duration = 12
group.repeatCount = MAXFLOAT
group.animations = [animation]
return group
}()
let strokeStartAnimation: CAAnimation = {
let animation = CABasicAnimation(keyPath: "strokeStart")
animation.beginTime = 2
animation.fromValue = 0
animation.toValue = 1
animation.duration = 10
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
let group = CAAnimationGroup()
group.duration = 12
group.repeatCount = MAXFLOAT
group.animations = [animation]
return group
}()
Thank You
Related
In my CALayer i am trying to animate images but the animation layer is not showing up. What i am doing wrong in here:
return CALayer().build {
let myLayer = CALayer()
let animation = CAKeyframeAnimation(keyPath: "contents")
animation.fillMode = kCAFillModeForwards
animation.duration = 3
animation.values = TimeLineVC.likeInteractionArray.map {
$0.cgImage as AnyObject
}
animation.repeatCount = 3
myLayer.frame = $0.bounds
myLayer.add(animation, forKey: "contents")
$0.insertSublayer(myLayer, above: $0)
myLayer.backgroundColor = UIColor.black.cgColor
}
It worked for me after adding a few properties:
let animation = CAKeyframeAnimation(keyPath: "contents")
animation.calculationMode = kCAAnimationDiscrete
animation.duration = 3.0
animation.repeatCount = .greatestFiniteMagnitude
animation.autoreverses = false
animation.values = TimeLineVC.likeInteractionArray.map {$0.cgImage!}
animation.isRemovedOnCompletion = false
animation.fillMode = kCAFillModeForwards
animation.beginTime = 0.0
$0.add(animation, forKey: "contents")
I have a single animation that animates a layer mask successfully:
// create new animation
let anim = CABasicAnimation(keyPath: "path")
anim.delegate = self
anim.fromValue = self.maskLayerMWB.path
anim.toValue = path2.CGPath
anim.duration = 0.2
anim.timingFunction = CAMediaTimingFunction(controlPoints: 0, 1, 1, 1)
self.maskLayerMWB.addAnimation(anim, forKey: nil)
CATransaction.begin()
CATransaction.setDisableActions(true)
self.maskLayerMWB.path = path2.CGPath
CATransaction.commit()
Above works fine. However, right after that, I need to add another CABasicAnimation that gets applied to a different CAShapeLayer.
However, when I add them together as follows, the second animation does not animate, only the first does:
// create new animation
let anim = CABasicAnimation(keyPath: "path")
anim.delegate = self
anim.fromValue = self.maskLayer.path
anim.toValue = path2.CGPath
anim.duration = 0.2
self.maskLayer.addAnimation(anim, forKey: nil)
//Create second Animation
let anim2 = CABasicAnimation(keyPath: "path")
anim2.delegate = self
anim2.fromValue = self.shapeLayer.path
anim2.toValue = path2.CGPath
anim2.duration = 0.2
self.shapeLayer.addAnimation(anim2, forKey: nil)
CATransaction.begin()
CATransaction.setDisableActions(true)
self.maskLayer.path = path2.CGPath
self.shapeLayer.path = path2.CGPath
CATransaction.commit()
I need both animations to happen simultaneously.
I was able to fix this by using CATransaction.setCompletionBlock
CATransaction.begin()
CATransaction.setDisableActions(true)
// create new animation
let anim = CABasicAnimation(keyPath: "path")
anim.delegate = self
anim.fromValue = self.maskLayer.path
anim.toValue = path2.CGPath
anim.duration = 0.2
self.maskLayer.addAnimation(anim, forKey: nil)
//Create second Animation
let anim2 = CABasicAnimation(keyPath: "path")
anim2.delegate = self
anim2.fromValue = self.shapeLayer.path
anim2.toValue = path2.CGPath
anim2.duration = 0.2
self.shapeLayer.addAnimation(anim2, forKey: nil)
CATransaction.setCompletionBlock({
self.maskLayer.path = path2.CGPath
self.shapeLayer.path = path2.CGPath
})
CATransaction.commit()
I have a UIView called viewProgress. It is the white box in the image. I want a circular progress bar, which is the green circle in the image. The progress bar should stay within the white box, but as you can see it is way off.
How do I make it stay inside the viewProgress?
Here you have my animation function:
func animateView() {
let circle = viewProgress // viewProgress is a UIView
var progressCircle = CAShapeLayer()
let circlePath = UIBezierPath(arcCenter: circle.center, radius: circle.bounds.midX, startAngle: -CGFloat(M_PI_2), endAngle: CGFloat(3.0 * M_PI_2), clockwise: true)
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath.CGPath
progressCircle.strokeColor = UIColor.whiteColor().CGColor
progressCircle.fillColor = UIColor.clearColor().CGColor
progressCircle.lineWidth = 10.0
circle.layer.addSublayer(progressCircle)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 0.4
animation.duration = 1
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
progressCircle.addAnimation(animation, forKey: "ani")
}
And I'm just calling the function inside viewDidLoad()
Basically u have few problems:
incorrect position of your layer - frame should be equal to bounds of parent layer in parentView
you animation will play only once - if u want to make it infinite add something like animation.repeatCount = MAXFLOAT
to make sure that all size correct - in layoutSubviews recreate layers, with removing previously created one
UIBezierPath(arcCenter... will start path from rightSide, u can also use simpler variant like UIBezierPath(ovalInRect. With this in mind u also need to rotate layer for M_PI_2 if u want to make start of drawing in the very top of circle
U should also take in mind that with line width 10, half of u'r circle path will be catted by clipSubviews of parentView. To prevent this use modified parentLayer rect
U can also maybe want to improve visualization of lineCap, buy setting it to "round" style
progressCircle.addAnimation(animation, forKey: "ani") key for anim not required if u dont want to get completion event from animation - i your case i see there is no delegate and no removeOnCompletion flag setted to false, so make assumption that u don`t actually need it
Keeping this all in mind code can be like :
let circle = UIView(frame: CGRectMake(100, 100, 100, 100)) // viewProgress is a UIView
circle.backgroundColor = UIColor.greenColor()
view.addSubview(circle)
var progressCircle = CAShapeLayer()
progressCircle.frame = view.bounds
let lineWidth:CGFloat = 10
let rectFofOval = CGRectMake(lineWidth / 2, lineWidth / 2, circle.bounds.width - lineWidth, circle.bounds.height - lineWidth)
let circlePath = UIBezierPath(ovalInRect: rectFofOval)
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath.CGPath
progressCircle.strokeColor = UIColor.whiteColor().CGColor
progressCircle.fillColor = UIColor.clearColor().CGColor
progressCircle.lineWidth = 10.0
progressCircle.frame = view.bounds
progressCircle.lineCap = "round"
circle.layer.addSublayer(progressCircle)
circle.transform = CGAffineTransformRotate(circle.transform, CGFloat(-M_PI_2))
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1.1
animation.duration = 1
animation.repeatCount = MAXFLOAT
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
progressCircle.addAnimation(animation, forKey: nil)
and result actually like:
Note:
If u want to animate rotation of animated layer u should also add additional animation in to anim group
I am trying to animate a gradient layer continuously. Please see the below gif?
I am using CAGradientLayer, but it only animates on color at a time.
var gradient: CAGradientLayer!
self.gradient = CAGradientLayer()
self.gradient.frame = CGRect(x: 0, y: 0, width:view.layer.frame.size.width, height: 50)
self.gradient.colors = [UIColor.redColor().CGColor, UIColor.redColor().CGColor]
self.view.layer.addSublayer(self.gradient)
self.animateLayer()
func animateLayer () {
let fromColors: NSArray = self.gradient.colors!
let toColors: NSArray = [UIColor.blueColor().CGColor, UIColor.blueColor().CGColor]
self.gradient.colors = toColors as [AnyObject]
let animation : CABasicAnimation = CABasicAnimation(keyPath: "colors")
animation.fromValue = fromColors
animation.toValue = toColors
animation.duration = 3.00
animation.removedOnCompletion = true
animation.fillMode = kCAFillModeForwards
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.delegate = self
self.gradient?.addAnimation(animation, forKey:"animateGradient")
}
I'm trying to get a UIImageView to shake indefinitely.
var coffeeImageView = UIImageView(image: UIImage(named: "coffee.png"))
coffeeImageView.frame = CGRectMake(100, self.view.frame.size.height - 100, 50, 50)
self.view.addSubview(coffeeImageView)
let coffeeShakeAnimation = CABasicAnimation(keyPath: "transform")
coffeeShakeAnimation.duration = 0.07
coffeeShakeAnimation.repeatCount = 20
coffeeShakeAnimation.autoreverses = true
coffeeShakeAnimation.fromValue = NSValue(CGPoint: CGPointMake(coffeeImageView.center.x - 10, coffeeImageView.center.y))
coffeeShakeAnimation.toValue = NSValue(CGPoint: CGPointMake(coffeeImageView.center.x + 10, coffeeImageView.center.y))
coffeeImageView.layer.addAnimation(coffeeShakeAnimation, forKey: "position")
replace
let coffeeShakeAnimation = CABasicAnimation(keyPath: "transform")
by
let coffeeShakeAnimation = CABasicAnimation(keyPath: "position")
it will shake