I'm trying to create a spinning circle loader animation like in the following Android project (https://github.com/pedant/sweet-alert-dialog).
I don't need the entire popup dialog - just the spinning part. It changes colors and spins indefinitly (until I choose to dismiss it).
I'm kind of new to swift and I've never been the kind to do animations. Here's what I have so far (found code in similar project for iOS):
The layers setup:
outlineLayer.position = CGPointMake(0, 0); outlineLayer.path = outlineCircle outlineLayer.fillColor = UIColor.clearColor().CGColor; outlineLayer.strokeColor = UIColor(red: 150.0/255.0, green: 216.0/255.0, blue: 115.0/255.0, alpha: 1.0).CGColor; outlineLayer.lineCap = kCALineCapRound outlineLayer.lineWidth = 4; outlineLayer.opacity = 0.1 self.layer.addSublayer(outlineLayer) circleLayer.position = CGPointMake(0, 0); circleLayer.path = path circleLayer.fillColor = UIColor.clearColor().CGColor; circleLayer.strokeColor = UIColor(red: 150.0/255.0, green: 216.0/255.0, blue: 115.0/255.0, alpha: 1.0).CGColor; circleLayer.lineCap = kCALineCapRound circleLayer.lineWidth = 4; circleLayer.actions = [ "strokeStart": NSNull(), "strokeEnd": NSNull(), "transform": NSNull() ] self.layer.addSublayer(circleLayer)
Animation:
let strokeStart = CABasicAnimation(keyPath: "strokeStart") let strokeEnd = CABasicAnimation(keyPath: "strokeEnd") let factor = 0.545 let timing = CAMediaTimingFunction(controlPoints: 0.3, 0.6, 0.8, 1.2) strokeEnd.fromValue = 0.00 strokeEnd.toValue = 0.93 strokeEnd.duration = 10.0 * factor strokeEnd.timingFunction = timing strokeEnd.autoreverses = true strokeStart.fromValue = 0.0 strokeStart.toValue = 0.68 strokeStart.duration = 10.0 * factor strokeStart.beginTime = CACurrentMediaTime() + 3.0 * factor strokeStart.fillMode = kCAFillModeBackwards strokeStart.timingFunction = timing strokeStart.repeatCount = HUGE circleLayer.strokeStart = 0.68 circleLayer.strokeEnd = 0.93 self.circleLayer.addAnimation(strokeEnd, forKey: "strokeEnd") self.circleLayer.addAnimation(strokeStart, forKey: "strokeStart")
but what I have is not nearly close and I have no idea where to go from here. What I'm doing is changing a value and running seeing how it affects but I feel like I'm lost here.
How can I achieve such animation like in the example?
Earlier, many web developers were using animated loading spinner .gifs. But CSS animations allow us to easily create a loading spinner. We are going to show how to create a simple circle spinning around its center. Create a <div> with a class name "spin" in the body section. Create a circle setting the width and the height of it.
Create a <div> with a class name "spin" in the body section. Create a circle setting the width and the height of it. Set the border-radius to 50% to make it rounded. Give color to the border. Give color to the spinner with the border-bottom-color property. Specify the animation, which has four values.
The loading spinner below is an SVG circle made up of three "dashes," which start as dots and then expand until they form a circle. The animation loops infinitely. What makes the loading spinner below unique is its gradient background color. It also contains span elements, which have their own gradient background colors.
The loading spinner below is animated to spin at a speed of two seconds, infinitely. It also contains a div with the word "Loading" that's also animated to become fully transparent and fully opaque as the loader spins. The loading spinner below is an SVG circle made up of three "dashes," which start as dots and then expand until they form a circle.
I didn't closely analyze the exact parameters of the animation, but this looks good to me:
import UIKit @IBDesignable class SpinnerView : UIView { override var layer: CAShapeLayer { get { return super.layer as! CAShapeLayer } } override class var layerClass: AnyClass { return CAShapeLayer.self } override func layoutSubviews() { super.layoutSubviews() layer.fillColor = nil layer.strokeColor = UIColor.black.cgColor layer.lineWidth = 3 setPath() } override func didMoveToWindow() { animate() } private func setPath() { layer.path = UIBezierPath(ovalIn: bounds.insetBy(dx: layer.lineWidth / 2, dy: layer.lineWidth / 2)).cgPath } struct Pose { let secondsSincePriorPose: CFTimeInterval let start: CGFloat let length: CGFloat init(_ secondsSincePriorPose: CFTimeInterval, _ start: CGFloat, _ length: CGFloat) { self.secondsSincePriorPose = secondsSincePriorPose self.start = start self.length = length } } class var poses: [Pose] { get { return [ Pose(0.0, 0.000, 0.7), Pose(0.6, 0.500, 0.5), Pose(0.6, 1.000, 0.3), Pose(0.6, 1.500, 0.1), Pose(0.2, 1.875, 0.1), Pose(0.2, 2.250, 0.3), Pose(0.2, 2.625, 0.5), Pose(0.2, 3.000, 0.7), ] } } func animate() { var time: CFTimeInterval = 0 var times = [CFTimeInterval]() var start: CGFloat = 0 var rotations = [CGFloat]() var strokeEnds = [CGFloat]() let poses = type(of: self).poses let totalSeconds = poses.reduce(0) { $0 + $1.secondsSincePriorPose } for pose in poses { time += pose.secondsSincePriorPose times.append(time / totalSeconds) start = pose.start rotations.append(start * 2 * .pi) strokeEnds.append(pose.length) } times.append(times.last!) rotations.append(rotations[0]) strokeEnds.append(strokeEnds[0]) animateKeyPath(keyPath: "strokeEnd", duration: totalSeconds, times: times, values: strokeEnds) animateKeyPath(keyPath: "transform.rotation", duration: totalSeconds, times: times, values: rotations) animateStrokeHueWithDuration(duration: totalSeconds * 5) } func animateKeyPath(keyPath: String, duration: CFTimeInterval, times: [CFTimeInterval], values: [CGFloat]) { let animation = CAKeyframeAnimation(keyPath: keyPath) animation.keyTimes = times as [NSNumber]? animation.values = values animation.calculationMode = .linear animation.duration = duration animation.repeatCount = Float.infinity layer.add(animation, forKey: animation.keyPath) } func animateStrokeHueWithDuration(duration: CFTimeInterval) { let count = 36 let animation = CAKeyframeAnimation(keyPath: "strokeColor") animation.keyTimes = (0 ... count).map { NSNumber(value: CFTimeInterval($0) / CFTimeInterval(count)) } animation.values = (0 ... count).map { UIColor(hue: CGFloat($0) / CGFloat(count), saturation: 1, brightness: 1, alpha: 1).cgColor } animation.duration = duration animation.calculationMode = .linear animation.repeatCount = Float.infinity layer.add(animation, forKey: animation.keyPath) } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With