[英]Custom UIView: animate subLayers with delay
I want to create a custom UIView subclass representing a bunch of stars on a dark-blue sky. 我想创建一个自定义UIView子类,该子类代表深蓝色天空中的一堆星星。
Therefore, I created this view: 因此,我创建了以下视图:
import UIKit
class ConstellationView: UIView {
// MARK: - Properties
@IBInspectable var numberOfStars: Int = 80
@IBInspectable var animated: Bool = false
// Private properties
private var starsToDraw = [CAShapeLayer]()
// Layers
private let starsLayer = CAShapeLayer()
// MARK: - Drawing
// override func drawRect(rect: CGRect) {
override func layoutSubviews() {
// Generate stars
drawStars(rect: self.bounds)
}
/// Generate stars
func drawStars(rect: CGRect) {
let width = rect.size.width
let height = rect.size.height
let screenBounds = UIScreen.main.bounds
// Create the stars and store them in starsToDraw array
for _ in 0 ..< numberOfStars {
let x = randomFloat() * width
let y = randomFloat() * height
// Calculate the thinness of the stars as a percentage of the screen resolution
let thin: CGFloat = max(screenBounds.width, screenBounds.height) * 0.003 * randomFloat()
let starLayer = CAShapeLayer()
starLayer.path = UIBezierPath(ovalIn: CGRect(x: x, y: y, width: thin, height: thin)).cgPath
starLayer.fillColor = UIColor.white.cgColor
starsToDraw.append(starLayer)
}
// Define a fade animation
let appearAnimation = CABasicAnimation(keyPath: "opacity")
appearAnimation.fromValue = 0.2
appearAnimation.toValue = 1
appearAnimation.duration = 1
appearAnimation.fillMode = kCAFillModeForwards
// Add the animation to each star (if animated)
for (index, star) in starsToDraw.enumerated() {
if animated {
// Add 1 s between each animation
appearAnimation.beginTime = CACurrentMediaTime() + TimeInterval(index)
star.add(appearAnimation, forKey: nil)
}
starsLayer.insertSublayer(star, at: 0)
}
// Add the stars layer to the view layer
layer.insertSublayer(starsLayer, at: 0)
}
private func randomFloat() -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UINT32_MAX)
}
}
It works quite well, here is the result: 它工作得很好,结果如下:
However, I'd like to have it animated, that is, each one of the 80 stars should appear one after the other, with a 1 second delay. 但是,我想让它动起来,也就是说,这80颗星星中的每一颗都应该一个接一个地出现,并且要延迟1秒。
I tried to increase the beginTime
of my animation, but it does not seem to do the trick. 我试图增加动画的beginTime
,但似乎并没有达到目的。 I checked with drawRect
or layoutSubviews
, but there is no difference. 我检查了drawRect
或layoutSubviews
,但没有区别。
Could you help me ? 你可以帮帮我吗 ?
Thanks 谢谢
PS: to reproduce my app, just create a new single view app in XCode, create a new file with this code, and set the ViewController's view as a ConstellationView, with a dark background color. PS:要重现我的应用程序,只需在XCode中创建一个新的单视图应用程序,使用此代码创建一个新文件,然后将ViewController的视图设置为ConstellationView,并使用深色背景色。 Also set the animated
property to true, either in Interface Builder, or in the code. 还要在Interface Builder或代码中将animated
属性设置为true。
PPS: this is in Swift 3, but I think it's still comprehensible :-) PPS:这是在Swift 3中,但我认为它仍然可以理解:-)
You're really close, only two things to do! 您真的很亲密,只有两件事要做!
First, you need to specify the key when you add the animation to the layer. 首先,在将动画添加到图层时需要指定关键点。
star.add(appearAnimation, forKey: "opacity")
Second, the fill mode for the animation needs to be kCAFillModeBackwards
instead of kCAFillModeForwards
. 其次,动画的填充模式需要为kCAFillModeBackwards
而不是kCAFillModeForwards
。
For a more detailed reference see - https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/AdvancedAnimationTricks/AdvancedAnimationTricks.html 有关更详细的参考,请参见-https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/AdvancedAnimationTricks/AdvancedAnimationTricks.html
And here's a fun tutorial (for practice with CAAnimations!) - https://www.raywenderlich.com/102590/how-to-create-a-complex-loading-animation-in-swift 这里还有一个有趣的教程(与CAAnimations实践!) - https://www.raywenderlich.com/102590/how-to-create-a-complex-loading-animation-in-swift
Hope this helps 😀 希望这可以帮助😀
Full Code: 完整代码:
class ConstellationView: UIView {
// MARK: - Properties
@IBInspectable var numberOfStars: Int = 80
@IBInspectable var animated: Bool = true
// Private properties
private var starsToDraw = [CAShapeLayer]()
// Layers
private let starsLayer = CAShapeLayer()
override func awakeFromNib() {
super.awakeFromNib()
}
// MARK: - Drawing
override func layoutSubviews() {
// Generate stars
drawStars(rect: self.bounds)
}
/// Generate stars
func drawStars(rect: CGRect) {
let width = rect.size.width
let height = rect.size.height
let screenBounds = UIScreen.main.bounds
// Create the stars and store them in starsToDraw array
for _ in 0 ..< numberOfStars {
let x = randomFloat() * width
let y = randomFloat() * height
// Calculate the thinness of the stars as a percentage of the screen resolution
let thin: CGFloat = max(screenBounds.width, screenBounds.height) * 0.003 * randomFloat()
let starLayer = CAShapeLayer()
starLayer.path = UIBezierPath(ovalIn: CGRect(x: x, y: y, width: thin, height: thin)).cgPath
starLayer.fillColor = UIColor.white.cgColor
starsToDraw.append(starLayer)
}
// Define a fade animation
let appearAnimation = CABasicAnimation(keyPath: "opacity")
appearAnimation.fromValue = 0.2
appearAnimation.toValue = 1
appearAnimation.duration = 1
appearAnimation.fillMode = kCAFillModeBackwards
// Add the animation to each star (if animated)
for (index, star) in starsToDraw.enumerated() {
if animated {
// Add 1 s between each animation
appearAnimation.beginTime = CACurrentMediaTime() + TimeInterval(index)
star.add(appearAnimation, forKey: "opacity")
}
starsLayer.insertSublayer(star, above: nil)
}
// Add the stars layer to the view layer
layer.insertSublayer(starsLayer, above: nil)
}
private func randomFloat() -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UINT32_MAX)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.