如何为倒数计时器创建循环进度指示器
How to create a circular progress indicator for a count down timer
我正在尝试向我的应用添加倒计时,但我在设置动画时遇到了问题。我想要的外观类似于下面显示的 iPad 倒计时,红色条随着时钟倒计时而增加。
最初我创建了一个图像集,其中倒计时的每半秒都有一张单独的图像,但这似乎会占用大量内存 运行 并因此导致应用程序崩溃,所以我现在必须找到替代方案.
我一直在看这里的着色 https://developer.apple.com/library/prerelease/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Sprites/Sprites.html#//apple_ref/doc/uid/TP40013043-CH9 但是看不到给精灵的一部分着色的方法,似乎整个精灵都会被改变。
有谁知道着色是否是这里的一个选项,或者是否有减少图像图集使用的内存的方法?
谢谢
您可以使用 CAShapeLayer 并为笔画末端设置动画,如下所示:
更新 Xcode 9 • Swift 4
定义剩余时间
let timeLeftShapeLayer = CAShapeLayer()
let bgShapeLayer = CAShapeLayer()
var timeLeft: TimeInterval = 60
var endTime: Date?
var timeLabel = UILabel()
var timer = Timer()
// here you create your basic animation object to animate the strokeEnd
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
定义一个方法来创建 de UIBezierPath
startAngle 在 -90˚ 和 endAngle 270
func drawBgShape() {
bgShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
bgShapeLayer.strokeColor = UIColor.white.cgColor
bgShapeLayer.fillColor = UIColor.clear.cgColor
bgShapeLayer.lineWidth = 15
view.layer.addSublayer(bgShapeLayer)
}
func drawTimeLeftShape() {
timeLeftShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
timeLeftShapeLayer.strokeColor = UIColor.red.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = 15
view.layer.addSublayer(timeLeftShapeLayer)
}
添加您的标签
func addTimeLabel() {
timeLabel = UILabel(frame: CGRect(x: view.frame.midX-50 ,y: view.frame.midY-25, width: 100, height: 50))
timeLabel.textAlignment = .center
timeLabel.text = timeLeft.time
view.addSubview(timeLabel)
}
在 viewDidload 设置结束时间并将您的 CAShapeLayer 添加到您的视图:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.94, alpha: 1.0)
drawBgShape()
drawTimeLeftShape()
addTimeLabel()
// here you define the fromValue, toValue and duration of your animation
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = timeLeft
// add the animation to your timeLeftShapeLayer
timeLeftShapeLayer.add(strokeIt, forKey: nil)
// define the future end time by adding the timeLeft to now Date()
endTime = Date().addingTimeInterval(timeLeft)
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
更新时间时
@objc func updateTime() {
if timeLeft > 0 {
timeLeft = endTime?.timeIntervalSinceNow ?? 0
timeLabel.text = timeLeft.time
} else {
timeLabel.text = "00:00"
timer.invalidate()
}
}
您可以使用此扩展程序将度数转换为弧度并显示时间
extension TimeInterval {
var time: String {
return String(format:"%02d:%02d", Int(self/60), Int(ceil(truncatingRemainder(dividingBy: 60))) )
}
}
extension Int {
var degreesToRadians : CGFloat {
return CGFloat(self) * .pi / 180
}
}
很棒的代码和答案!只是想我会建议以下内容。我得到的时间显示为 1:00,然后是 0:60,我认为这可能是由于代码的 "ceil" 部分造成的。
我把它改成下面的(也希望小于 60 的时间显示不同)并且它似乎已经消除了时间的重叠。
物有所值...
extension NSTimeInterval {
var time:String {
if self < 60 {
return String(format: "%02d", Int(floor(self%60)))
} else
{
return String(format: "%01d:%02d", Int(self/60), Int(floor(self%60)))
}
}
}
感谢 ,我看到有人问为什么代码不起作用/无法在他们自己的项目中实施。以下是使用 CABasicAnimation 时要记住的一些提示:
- 我们应该将动画添加到视图中已经存在的层,即确保在视图之后调用 layer.add。layer.add子层。
- 尽量不要在viewDidLoad中做动画,因为我们无法判断图层是否已经存在。
所以我们可以尝试这样的事情:
override func viewDidLoad() {
super.viewDidLoad()
drawBgShape()
drawTimeLeftShape()
...
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// move the setup of animation here
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = timeLeft
timeLeftShapeLayer.add(strokeIt, forKey: nil)
}
附上一些很好的参考资料,帮助我阐明这个想法。希望这对您有所帮助,祝您有个愉快的一天 ;)
我正在尝试向我的应用添加倒计时,但我在设置动画时遇到了问题。我想要的外观类似于下面显示的 iPad 倒计时,红色条随着时钟倒计时而增加。
最初我创建了一个图像集,其中倒计时的每半秒都有一张单独的图像,但这似乎会占用大量内存 运行 并因此导致应用程序崩溃,所以我现在必须找到替代方案.
我一直在看这里的着色 https://developer.apple.com/library/prerelease/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Sprites/Sprites.html#//apple_ref/doc/uid/TP40013043-CH9 但是看不到给精灵的一部分着色的方法,似乎整个精灵都会被改变。
有谁知道着色是否是这里的一个选项,或者是否有减少图像图集使用的内存的方法?
谢谢
您可以使用 CAShapeLayer 并为笔画末端设置动画,如下所示:
更新 Xcode 9 • Swift 4
定义剩余时间
let timeLeftShapeLayer = CAShapeLayer()
let bgShapeLayer = CAShapeLayer()
var timeLeft: TimeInterval = 60
var endTime: Date?
var timeLabel = UILabel()
var timer = Timer()
// here you create your basic animation object to animate the strokeEnd
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
定义一个方法来创建 de UIBezierPath
startAngle 在 -90˚ 和 endAngle 270
func drawBgShape() {
bgShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
bgShapeLayer.strokeColor = UIColor.white.cgColor
bgShapeLayer.fillColor = UIColor.clear.cgColor
bgShapeLayer.lineWidth = 15
view.layer.addSublayer(bgShapeLayer)
}
func drawTimeLeftShape() {
timeLeftShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
timeLeftShapeLayer.strokeColor = UIColor.red.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = 15
view.layer.addSublayer(timeLeftShapeLayer)
}
添加您的标签
func addTimeLabel() {
timeLabel = UILabel(frame: CGRect(x: view.frame.midX-50 ,y: view.frame.midY-25, width: 100, height: 50))
timeLabel.textAlignment = .center
timeLabel.text = timeLeft.time
view.addSubview(timeLabel)
}
在 viewDidload 设置结束时间并将您的 CAShapeLayer 添加到您的视图:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.94, alpha: 1.0)
drawBgShape()
drawTimeLeftShape()
addTimeLabel()
// here you define the fromValue, toValue and duration of your animation
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = timeLeft
// add the animation to your timeLeftShapeLayer
timeLeftShapeLayer.add(strokeIt, forKey: nil)
// define the future end time by adding the timeLeft to now Date()
endTime = Date().addingTimeInterval(timeLeft)
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
更新时间时
@objc func updateTime() {
if timeLeft > 0 {
timeLeft = endTime?.timeIntervalSinceNow ?? 0
timeLabel.text = timeLeft.time
} else {
timeLabel.text = "00:00"
timer.invalidate()
}
}
您可以使用此扩展程序将度数转换为弧度并显示时间
extension TimeInterval {
var time: String {
return String(format:"%02d:%02d", Int(self/60), Int(ceil(truncatingRemainder(dividingBy: 60))) )
}
}
extension Int {
var degreesToRadians : CGFloat {
return CGFloat(self) * .pi / 180
}
}
很棒的代码和答案!只是想我会建议以下内容。我得到的时间显示为 1:00,然后是 0:60,我认为这可能是由于代码的 "ceil" 部分造成的。
我把它改成下面的(也希望小于 60 的时间显示不同)并且它似乎已经消除了时间的重叠。
物有所值...
extension NSTimeInterval {
var time:String {
if self < 60 {
return String(format: "%02d", Int(floor(self%60)))
} else
{
return String(format: "%01d:%02d", Int(self/60), Int(floor(self%60)))
}
}
}
感谢
- 我们应该将动画添加到视图中已经存在的层,即确保在视图之后调用 layer.add。layer.add子层。
- 尽量不要在viewDidLoad中做动画,因为我们无法判断图层是否已经存在。
所以我们可以尝试这样的事情:
override func viewDidLoad() {
super.viewDidLoad()
drawBgShape()
drawTimeLeftShape()
...
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// move the setup of animation here
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = timeLeft
timeLeftShapeLayer.add(strokeIt, forKey: nil)
}
附上一些很好的参考资料,帮助我阐明这个想法。希望这对您有所帮助,祝您有个愉快的一天 ;)