创建旋转圈加载动画 [英] Create spinning circle loading animation

查看:54
本文介绍了创建旋转圈加载动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试像下面的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆