xcode 如何使循环加载动画颜色从红色到绿色,因为它加载

r7knjye2  于 2023-03-04  发布在  其他
关注(0)|答案(1)|浏览(107)

我已经实现了一个循环加载动画,与此视频中的完全相同:
https://www.youtube.com/watch?v=O3ltwjDJaMk
下面是我的代码:

class ViewController: UIViewController {

let shapeLayer = CAShapeLayer()

override func viewDidLoad() {
        super.viewDidLoad()
        
        let center = view.center
        let trackLayer = CAShapeLayer()
        
        let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
        trackLayer.path = circularPath.cgPath
        trackLayer.strokeColor = UIColor.lightGray.cgColor
        trackLayer.lineWidth = 10
        trackLayer.fillColor = UIColor.clear.cgColor
        trackLayer.lineCap = kCALineCapRound
        view.layer.addSublayer(trackLayer)
        
        shapeLayer.path = circularPath.cgPath
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineCap = kCALineCapRound
        shapeLayer.strokeEnd = 0
     
        view.layer.addSublayer(shapeLayer)
        
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
    }
    
    @objc private func handleTap() {
 
        let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
        
        basicAnimation.toValue = 1
        basicAnimation.duration = 2
        
        basicAnimation.fillMode = kCAFillModeForwards
        basicAnimation.isRemovedOnCompletion = false
        
        shapeLayer.add(basicAnimation, forKey: "urSoBasic")
    }
}

当动画发生时,我希望圆圈的颜色从红色变成绿色,如果圆圈完全完成,它应该是绿色的,如果没有,那么一些颜色之间的红色和绿色(取决于变量从用户名)。

kulphzqa

kulphzqa1#

你也可以使用CAAnimationanimate color,所以你可以点击那个链接。
缺少的部分是计算fromto颜色,需要根据当前值进行插值。
假设您的进度视图将在某个时间点更新界面以具有

var minimumValue: CGFloat = 0.0
var maximumValue: CGFloat = 100.0
var currentValue: CGFloat = 30.0 // 30%

然后使用这些值可以计算当前进度,一个介于01之间的值,该值应定义颜色插值比例。计算该值的基本数学公式应为:

let progress = (currentValue-maximumValue)/(minimumValue-maximumValue) // TODO: handle division by zero. Handle current value out of bounds.

随着进度的推进,您现在可以使用以下公式插入任何数值

func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
    return values.from + (values.to - values.from)*scale
}

但是颜色不是一个数值。在CAAnimation的情况下,你应该把它分解成4个数值作为RGBA以保持一致性(至少我相信CAAnimation在RGBA空间中使用插值来获得颜色)。在许多情况下,当你在HSV空间而不是RGB空间内插时,它看起来更好。
因此,对颜色进行插值应如下所示:

func rgba(_ color: UIColor) -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
    var r: CGFloat = 0.0
    var g: CGFloat = 0.0
    var b: CGFloat = 0.0
    var a: CGFloat = 0.0
    color.getRed(&r, green: &g, blue: &b, alpha: &a)
    return (r, g, b, a)
}
func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
    return values.from + (values.to - values.from)*scale
}
func interpolate(_ values: (from: UIColor, to: UIColor), scale: CGFloat) -> UIColor {
    let startColorComponents = rgba(values.from)
    let endColorComponents = rgba(values.to)
    return .init(red: interpolate((startColorComponents.r, endColorComponents.r), scale: scale),
                 green: interpolate((startColorComponents.g, endColorComponents.g), scale: scale),
                 blue: interpolate((startColorComponents.b, endColorComponents.b), scale: scale),
                 alpha: interpolate((startColorComponents.a, endColorComponents.a), scale: scale))
}

这些应该是你制作动画所需要的所有组件,我希望这些组件足以让你走上正确的道路。我个人喜欢有更多的控制权,而且我喜欢避免使用CAAnimation甚至是形状层等工具。所以这就是我将如何完成你的任务:

class ViewController: UIViewController {

    @IBOutlet private var progressView: ProgressView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
     
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
    }
    
    @objc private func onTap() {
        guard let progressView = progressView else { return }
        progressView.animateValue(to: .random(in: progressView.minimumValue...progressView.maximumValue), duration: 0.3)
    }

}

@IBDesignable class ProgressView: UIView {
    
    @IBInspectable var minimumValue: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
    @IBInspectable var maximumValue: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
    @IBInspectable var value: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
    @IBInspectable var colorAtZeroProgress: UIColor = .black { didSet { setNeedsDisplay() } }
    @IBInspectable var colorAtFullProgress: UIColor = .black { didSet { setNeedsDisplay() } }
    @IBInspectable var lineWidth: CGFloat = 10.0 { didSet { setNeedsDisplay() } }
    
    private var currentTimer: Timer?
    func animateValue(to: CGFloat, duration: TimeInterval) {
        currentTimer?.invalidate()
        let startTime = Date()
        let startValue = self.value
        currentTimer = Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true, block: { timer in
            let progress = CGFloat(max(0.0, min(Date().timeIntervalSince(startTime)/duration, 1.0)))
            if progress >= 1.0 {
                // End of animation
                timer.invalidate()
            }
            self.value = startValue + (to - startValue)*progress
        })
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
         
        let progress = max(0.0, min((value-minimumValue)/(maximumValue-minimumValue), 1.0))
        
        let center = CGPoint(x: bounds.midX, y: bounds.midY)
        let radius = min(bounds.width, bounds.height)*0.5 - lineWidth*0.5
        let startAngle: CGFloat = -.pi*2.0
        let endAngle: CGFloat = startAngle + .pi*2.0*progress
        let circularPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        
        
        
        let interpolatedColor: UIColor = {
            func rgba(_ color: UIColor) -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
                var r: CGFloat = 0.0
                var g: CGFloat = 0.0
                var b: CGFloat = 0.0
                var a: CGFloat = 0.0
                color.getRed(&r, green: &g, blue: &b, alpha: &a)
                return (r, g, b, a)
            }
            func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
                return values.from + (values.to - values.from)*scale
            }
            func interpolate(_ values: (from: UIColor, to: UIColor), scale: CGFloat) -> UIColor {
                let startColorComponents = rgba(values.from)
                let endColorComponents = rgba(values.to)
                return .init(red: interpolate((startColorComponents.r, endColorComponents.r), scale: scale),
                             green: interpolate((startColorComponents.g, endColorComponents.g), scale: scale),
                             blue: interpolate((startColorComponents.b, endColorComponents.b), scale: scale),
                             alpha: interpolate((startColorComponents.a, endColorComponents.a), scale: scale))
            }
            
            return interpolate((self.colorAtZeroProgress, self.colorAtFullProgress), scale: progress)
        }()
        
        interpolatedColor.setStroke()
        circularPath.lineCapStyle = .round
        circularPath.lineWidth = lineWidth
        circularPath.stroke()
    }
    
    
}

请随意使用和修改它,无论你喜欢。

相关问题