我在其外部有一个带有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()
}
}
当计时器运行时,红色shapeLayer将填充外部的灰色圆圈。因为没有标签显示计时器正在运行,所以应该指示用户还有多少时间要记录。
答案 0 :(得分:0)
此示例进度轮涉及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, radius: CGFloat, weight: 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, radius: radius, weight: weight, view: view)
let strokeView1 = StrokeView(frame: rect, strokeColor: strokeColor1, start: start1, end: end1, radius: radius, weight: weight, view: view)
let strokeView2 = StrokeView(frame: rect, strokeColor: strokeColor2, start: start2, end: end2, radius: radius, weight: weight, 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()
}
}
如何通过视图控制器运行进度轮
在这种情况下,向用户显示开始按钮。当他们点击它时,将出现一个进度轮。
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()