自定义 UIView:延迟动画 subLayers
Custom UIView: animate subLayers with delay
我想创建一个自定义的 UIView 子类来表示深蓝色天空中的一串星星。
因此,我创建了这个视图:
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)
}
}
效果很好,这是结果:
不过,我想让它有动画效果,也就是说,80颗星星中的每一颗都应该一个接一个地出现,延迟1秒。
我试图增加动画的 beginTime
,但它似乎并没有奏效。
我用drawRect
或layoutSubviews
检查过,但没有区别。
你能帮帮我吗?
谢谢
PS:要重现我的应用程序,只需在 XCode 中创建一个新的单一视图应用程序,使用此代码创建一个新文件,并将 ViewController 的视图设置为一个 ConstellationView,具有深色背景。还要在 Interface Builder 或代码中将 animated
属性 设置为 true。
PPS:这个在Swift3,不过我觉得还是可以理解的:-)
你真的很接近,只有两件事要做!
首先在图层中添加动画时需要指定key
star.add(appearAnimation, forKey: "opacity")
其次,动画的填充模式需要是kCAFillModeBackwards
而不是kCAFillModeForwards
。
这是一个有趣的教程(用于练习 CAAnimations!)- https://www.raywenderlich.com/102590/how-to-create-a-complex-loading-animation-in-swift
希望对您有所帮助
完整代码:
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)
}
}
我想创建一个自定义的 UIView 子类来表示深蓝色天空中的一串星星。
因此,我创建了这个视图:
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)
}
}
效果很好,这是结果:
不过,我想让它有动画效果,也就是说,80颗星星中的每一颗都应该一个接一个地出现,延迟1秒。
我试图增加动画的 beginTime
,但它似乎并没有奏效。
我用drawRect
或layoutSubviews
检查过,但没有区别。
你能帮帮我吗?
谢谢
PS:要重现我的应用程序,只需在 XCode 中创建一个新的单一视图应用程序,使用此代码创建一个新文件,并将 ViewController 的视图设置为一个 ConstellationView,具有深色背景。还要在 Interface Builder 或代码中将 animated
属性 设置为 true。
PPS:这个在Swift3,不过我觉得还是可以理解的:-)
你真的很接近,只有两件事要做!
首先在图层中添加动画时需要指定key
star.add(appearAnimation, forKey: "opacity")
其次,动画的填充模式需要是kCAFillModeBackwards
而不是kCAFillModeForwards
。
这是一个有趣的教程(用于练习 CAAnimations!)- https://www.raywenderlich.com/102590/how-to-create-a-complex-loading-animation-in-swift
希望对您有所帮助
完整代码:
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)
}
}