创建旋转圈加载动画 [英] Create spinning circle loading animation
问题描述
我正在尝试像下面的Android项目中那样创建旋转的圆圈加载器动画(
我不需要整个弹出对话框-只是旋转的部分。它会改变颜色并无限旋转(直到我选择将其消除)。
我是个新手,我从来都不喜欢做动画。这是我到目前为止(在iOS的类似项目中找到代码):
图层设置:
outlineLayer.position = CGPointMake(0,
0);
outlineLayer.path = outlineCircle
outlineLayer.fillColor = UIColor.clearColor()。CGColor;
outlineLayer.strokeColor = UIColor(红色:150.0 / 255.0,绿色:216.0 / 255.0,蓝色: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 =路径
circleLayer.fillColor = UIColor.clearColor()。CGColor;
circleLayer.strokeColor = UIColor(红色:150.0 / 255.0,绿色:216.0 / 255.0,蓝色: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)
动画:
let strokeStart = CABasicAnimation(keyPath: strokeStart)
let strokeEnd = CABasicAnimation(keyPath: strokeEnd)
let factor = 0.545
let时序= CAMediaTimingFunction(controlPoints:0.3、0.6、0.8、1.2)
strokeEnd.fromValue = 0.00
strokeEnd.toValue = 0.93
strokeEnd.duration = 10.0 *因子
strokeEnd.timingFunction =定时
strokeEnd.autoreverses = true
strokeStart.fromValue = 0.0
strokeStart.toValue = 0.68
strokeStart.duration = 10.0 *因子
strokeStart.beginTime = CACurrentMediaTime()+ 3.0 *因子
strokeStart.fillMode = kCAFillModeBackwards
strokeStart.timingFunction =定时
strokeStart.repeatCount =巨大
circleLayer.strokeStart = 0.68
circleLayer.strokeEnd = 0.93
self.circleLayer.addAnimation(strokeEnd,forKey: strokeEnd)
self.circleLayer.addAnimation(strokeStart,forKey: strokeStart)
但是我有什么距离还不很近,我也不知道从这里去哪里。我正在做的是更改值并运行以查看其影响,但是我感觉自己在这里迷失了。
如何实现如示例中所示的动画?
我没有仔细分析动画的确切参数,但这对我来说很好:
导入UIKit
@IBDesignable
类SpinnerView:UIView {
覆盖var层:CAShapeLayer {
get {
返回super.layer为! CAShapeLayer
}
}
覆盖类var layerClass:AnyClass {
return CAShapeLayer.self
}
覆盖func layoutSubviews (){
super.layoutSubviews()
layer.fillColor = nil
layer.strokeColor = UIColor.black.cgColor
layer.lineWidth = 3
setPath()
}
覆盖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
让我们开始:CGFloat
让长度:CGFloat
init(_ secondsSincePriorPose:CFTimeInterval,_开始:CGFloat,_长度:CGFloat){
self.secondsSincePriorPose = secondsSincePriorPose
self.start =开始
self.length =长度
}
}
类变量:[Pose] {
get {
return [
Pose(0.0 ,0.000,0.7),
姿势(0.6,0.500,0.5),
姿势(0.6,1.000,0.3),
姿势(0.6,1.500,0.1),
姿势(0.2,1.875,0.1),
姿势(0.2,2.250,0.3),
姿势(0.2,2.625,0.5),
姿势(0.2,3.000,0.7),
]
}
}
func animate(){
var time:CFTimeInterval = 0
var times = [CFTimeInterval]()
var开始:CGFloat = 0
var rotations = [CGFloat]()
var strokeEnds = [CGFloat]()
letposes = type(of:self).poses
let totalSeconds = pose.reduce(0){$ 0 + $ 1.secondsSincePriorPose}
表示姿势中的姿势{
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,持续时间:totalSeconds,时间:次,值:strokeEnds)
animateKeyPath(keyPath: transform.rotation,持续时间:totalSeconds,时间:次,值:rotations)
animateStrokeHueWithDuration(duration:totalSeconds * 5)
}
func animateKeyPath(keyPath:String,duration:CFTimeInterval,时间:[CFTimeInterval],值:[CGFloat ]){
let animation = CAKeyframeAnimation(keyPath:keyPath)
animation.keyTimes =倍于[NSNumber]?
animation.values =值
animation.calculationMode = .linear
animation.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(色调:CGFloat($ 0)/ CGFloat(count),饱和度:1,亮度:1,alpha:1).cgColor
}
animation.duration =持续时间
animation.calculationMode = .linear
animation.repeatCount = Float.infinity
layer.add(animation,forKey:animation.keyPath)
}
}
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?
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)
}
}
这篇关于创建旋转圈加载动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!