计时器运行时对CAShapeLayer进行动画处理

我在其外部有一个带有CAShapelayer的圆形按钮。我也有一个计时器,可以在视频播放时运行。随着计时器的运行,我想更新CAShapelayer,就像它是进度指示器一样。问题在于计时器运行时,shapelayer上的动画开始跳来跳去,它并不流畅。我试图在主队列上对其进行更新,但这也不起作用。

我要去哪里错了?

lazy var roundButton: UIButton = {
    // create button
}()

var seconds = 15
weak var videoTimer: Timer?
let shapelayer = CAShapelayer()
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")

func addCAShapelayerToButton() {

    // there is another shapelayer that is gray used as the outer circle background layer

    let circularPath = UIBezierPath(arcCenter: roundButton.center,radius: (roundButton.frame.width / 2) + 10,startAngle: -.pi/2,endAngle: 3 * .pi/2,clockwise: true)
    shapelayer.path = timerCircularPath.cgPath
    shapelayer.strokeColor = UIColor.red.cgColor
    shapelayer.fillColor = fillColor
    shapelayer.lineWidth = lineWidth
    shapelayer.strokeEnd = 0
    view.layer.addSublayer(shapelayer)

    basicAnimation.fillMode = CAMediaTimingFillMode.forwards
    basicAnimation.isRemovedOnCompletion = false   
    shapelayer.add(basicAnimation,forKey: nil)
}

func startTimer() {
        
    videoTimer?.invalidate()
    videoTimer = Timer.scheduledTimer(withTimeInterval: 1,repeats: true,block: { [weak self] _ in
        self?.timerIsRunning()
    })
    if let videoTimer = videoTimer {
        RunLoop.current.add(videoTimer,forMode: RunLoop.Mode.common)
    }
}

@objc fileprivate func timerIsRunning() {

    if seconds != 0 {
        seconds -= 1

        let recordingProgress: CGFloat = CGFloat(seconds) / CGFloat(15)
        shapelayer.strokeEnd = recordingProgress // tried updating on the mainqueue

        /*
          I also tried
          basicAnimation.fromValue = 0
          basicAnimation.toValue = 1
          basicAnimation.duration = CFTimeInterval(recordingProgress)
        */

    } else {

        videoTimer?.invalidate()
    }
}

计时器运行时对CAShapeLayer进行动画处理

当计时器运行时,红色shapelayer将填充外部的灰色圆圈。因为没有标签显示计时器正在运行,所以应该指示用户还有多少时间要记录。

iCMS 回答:计时器运行时对CAShapeLayer进行动画处理

此示例进度轮涉及UIView的子类和NSObjet的子类。前者创建轮视图,而后者在动画中执行Energizer兔子,直到用户停止它

StrokeView.swift

import UIKit

class StrokeView: UIView {
    var shapeLayer = CAShapeLayer()
    var strokeColor: UIColor // strokeColor
    var start: CGFloat
    var end: CGFloat
    var radius: CGFloat // radius
    var weight: CGFloat // weight
    var view: UIView // view
    
    init(frame: CGRect,strokeColor: UIColor,start: CGFloat,end: CGFloat,radius: CGFloat,weight: CGFloat,view: UIView){
        self.strokeColor = strokeColor
        self.start = start
        self.end = end
        self.radius = radius
        self.weight = weight
        self.view = view
        super.init(frame: frame)
        self.isOpaque = false
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        let bounds = self.bounds
        let centerPoint = CGPoint(x: bounds.width/2,y: bounds.height/2)
        let circlePath = UIBezierPath(arcCenter: centerPoint,radius: radius,startAngle: start,endAngle: end,clockwise: true)
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = strokeColor.cgColor
        shapeLayer.lineWidth = weight
        self.layer.addSublayer(shapeLayer)
    }
}

RotateMe.swift

import UIKit

class RotateMe: NSObject {
    var myView = UIView()
    
    var strokeColor0: UIColor
    var start0: CGFloat
    var end0: CGFloat
    
    var strokeColor1: UIColor
    var start1: CGFloat
    var end1: CGFloat
    
    var strokeColor2: UIColor
    var start2: CGFloat
    var end2: CGFloat
    
    var rect: CGRect
    var radius: CGFloat
    var weight: CGFloat
    var slowness: Double
    var view: UIView
    
    init(rect: CGRect,strokeColor0: UIColor,start0: CGFloat,end0: CGFloat,strokeColor1: UIColor,start1: CGFloat,end1: CGFloat,strokeColor2: UIColor,start2: CGFloat,end2: CGFloat,slowness: Double,view: UIView) {
        self.rect = rect
        self.strokeColor0 = strokeColor0
        self.start0 = start0
        self.end0 = end0
        self.strokeColor1 = strokeColor1
        self.start1 = start1
        self.end1 = end1
        self.strokeColor2 = strokeColor2
        self.start2 = start2
        self.end2 = end2
        self.radius = radius
        self.weight = weight
        self.slowness = slowness
        self.view = view
    }
    
    func circleMe() {
        let minSize = min(rect.width,rect.height)
        let myRect = CGRect(origin: CGPoint(x: (view.frame.width - minSize)/2.0,y: (view.frame.height - minSize)/2.0),size: CGSize(width: rect.width,height: rect.height))
        myView = UIView(frame: myRect)
        let strokeView0 = StrokeView(frame: rect,strokeColor: strokeColor0,start: start0,end: end0,weight: weight,view: view)
        let strokeView1 = StrokeView(frame: rect,strokeColor: strokeColor1,start: start1,end: end1,view: view)
        let strokeView2 = StrokeView(frame: rect,strokeColor: strokeColor2,start: start2,end: end2,view: view)
        myView.addSubview(strokeView0)
        myView.addSubview(strokeView1)
        myView.addSubview(strokeView2)
        //myView.backgroundColor = UIColor.blue.withAlphaComponent(0.4)
        
        let start = 0.0
        let end = start + 2 * Double.pi
        let dur = slowness as Double
        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotateAnimation.duration = dur
        rotateAnimation.repeatCount = .infinity
        rotateAnimation.fromValue = start
        rotateAnimation.toValue = end
        //rotateAnimation.removedOnCompletion = true;
        rotateAnimation.fillMode = CAMediaTimingFillMode.forwards;
        rotateAnimation.autoreverses = false
        rotateAnimation.isCumulative = true // if set it to no,the view won't rotate and will only move in an awkward way
        myView.layer.add(rotateAnimation,forKey: "rotationAnimation")
        view.addSubview(myView)
    }
    
    func stopRotation() {
        myView.removeFromSuperview()
    }
}

如何通过视图控制器运行进度轮

enter image description here

在这种情况下,向用户显示开始按钮。当他们点击它时,将出现一个进度轮。

enter image description here

import UIKit

class ViewController: UIViewController {
    // MARK: - Variables
    
    // MARK: - IBOutlet
    @IBOutlet var rotateView: RotateMe?
    @IBOutlet weak var startButton: UIButton!
    @IBOutlet weak var endButton: UIButton!
    
    // MARK: - IBAction
    @IBAction func startTapped(_ sender: UIButton) {
        startButton.isEnabled = false
        endButton.isEnabled = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
            self.makeProgress()
        }
    }
    
    @IBAction func endTapped(_ sender: UIButton) {
        startButton.isEnabled = true
        endButton.isEnabled = false
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
            self.rotateView?.stopRotation()
        }
    }
    
    // MARK: - Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    func makeProgress() {
        let selfFrame = view.frame
        let minFloat = min(selfFrame.size.width,selfFrame.size.height)
        let rect = CGRect(x: 0.0,y: 0.0,width: minFloat,height: minFloat)
        let color0 = UIColor.orange
        let start0 = CGFloat(-120.0).degreesToRadians()
        let end0 = CGFloat(0.0).degreesToRadians()
        let color1 = UIColor.purple
        let start1 = CGFloat(0.0).degreesToRadians()
        let end1 = CGFloat(120.0).degreesToRadians()
        
        let color2 = UIColor.green
        let start2 = CGFloat(120.0).degreesToRadians()
        let end2 = CGFloat(-120.0).degreesToRadians()
        
        rotateView = RotateMe(rect: rect,strokeColor0: color0,start0: start0,end0: end0,strokeColor1: color1,start1: start1,end1: end1,strokeColor2: color2,start2: start2,end2: end2,radius: 80.0,weight: 30.0,slowness: 2.0,view: view)
        rotateView?.circleMe()
    }
}

extension CGFloat {
    func degreesToRadians() -> CGFloat {
        return self * .pi / 180.0
    }
    
    func radiansToDegrees() -> CGFloat {
        return self * (180.0 / .pi)
    }
}

根据您的情况,运行Timer,时间15秒。然后只需致电

rotateView?.stopRotation()
本文链接:https://www.f2er.com/2043001.html

大家都在问