为什么我的动画比计时器快

  • 本文关键字:计时器 动画 swift
  • 更新时间 :
  • 英文 :


我有一个动画,我想与计时器重合,但现在它在计时器还剩6秒时结束。如何使动画匹配?此外,我将如何重复迭代倒计时的动画,i?

代码中有一个圆圈中的动画,然后预设30秒的计时器(最终将成为滑块输入(。我最终还想为计时器添加一个暂停和停止按钮,这将需要与动画一致

import UIKit
var timer = Timer()
var time = 30
var i = 5
class ViewController: UIViewController {
@IBOutlet weak var displayTime: UILabel!

let shape = CAShapeLayer()
private let label: UILabel = {
let label = UILabel()
label.text = String(i)
// change label to update i
label.font = .systemFont(ofSize: 36, weight: .light)
return label
}()
func countdown() {
displayTime.text = String(time)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: 
#selector(doCountdown), userInfo: nil, repeats: true)
}
override func viewDidLoad() {
super.viewDidLoad()
label.sizeToFit()
view.addSubview(label)
label.center = view.center

let circlePath = UIBezierPath(arcCenter: view.center, radius: 150, startAngle: - 
(.pi / 2), endAngle: .pi * 2, clockwise: true)

let trackShape = CAShapeLayer()
trackShape.path = circlePath.cgPath
trackShape.fillColor = UIColor.clear.cgColor
trackShape.lineWidth = 15
trackShape.strokeColor = UIColor.lightGray.cgColor
view.layer.addSublayer(trackShape)


shape.path = circlePath.cgPath
shape.lineWidth = 15
shape.strokeColor = UIColor.blue.cgColor
shape.fillColor = UIColor.clear.cgColor
shape.strokeEnd = 0
// cg = core graphics

view.layer.addSublayer(shape)

let button = UIButton(frame: CGRect(x: 20, y: view.frame.size.height-70, width: 
view.frame.size.width-40, height: 50))
view.addSubview(button)
button.setTitle("Animate", for: .normal)
button.backgroundColor = .systemGreen
button.addTarget(self, action:#selector(didTapButton), for: .touchUpInside)
}
@objc func didTapButton() {
countdown()
// Animate
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.toValue = 1
animation.duration = Double(time)
// duration of animation
animation.isRemovedOnCompletion = false
animation.fillMode = .forwards
shape.add(animation, forKey: "animation")
}
@objc func doCountdown() {
time = time - 1
displayTime.text = String(time)
if time == 0 {
i = i - 1
time = 30
}
if i == 0 {
label.text = "0"
timer.invalidate()
}
}
}

您的实现不起作用,因为您使用的是倒计时的天真实现。

定时器不能保证在给定的时间后准确地启动。它不会在一秒钟后发射。Timer的精度为50-100毫秒。因此,总的可能误差加起来可以达到30*100毫秒,也就是整整3秒。

相反,您必须使用Timer来更频繁地更新您的UI:

timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: 
#selector(doCountdown), userInfo: nil, repeats: true)

这也意味着你必须以不同的方式计算你的时间。首先,存储动画结束的预期时间:

// declare instance properties
private var animationEnd = Date()
private var timer: Timer? {
didSet {
// invalidate when nil is assigned
oldValue?.invalidate()
}
}
func startCountdown() {
// store the start time - 30 seconds in the future
animationEnd = Date().addingTimeInterval(TimerInterval(time))
// start the timer
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
guard let self = self else { return }
let remainingTime = max(0, self.animationEnd.timeIntervalSinceNow)
if remainingTime == 0 {
// invalidate the timer
self.timer = nil      
}
// convert time to seconds
let remaininingSeconds = Int(remainingTime) ?? 0
self.displayTime.text = "(remaininingSeconds)"
}
}
//

仅此而已。

如果你想暂停&恢复计时器,过程相同。使计时器无效,存储当前时间(例如timePaused = Date(,恢复时,只需将当前时间与timePaused之间的差值添加到animationEnd,然后重新启动计时器。

另外,请不要将变量放在文件级别。总是把它们放在类的范围内。否则你很快就会遇到问题。

我认为这两个变量是问题的根源-

var time = 30
var i = 5

你可以尝试删除i变量并使用这个更新的实现-吗

@objc func doCountdown() {
time -= 1
displayTime.text = String(time)
if time == 0 {
timer.invalidate()
}
}

最新更新