2016-09-21 101 views
0

我想创建一个自定义的UIView子类,代表一群星星在深蓝色的天空。自定义UIView:延迟动画subLayers

因此,我创造了这样的观点:

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) 
    } 

} 

那很好,这里是结果: enter image description here

不过,我想有它的动画,也就是说,每一个的80颗星应该一个接一个地出现,延迟1秒。

我试图增加我的动画的beginTime,但它似乎没有办法。 我检查了drawRectlayoutSubviews,但没有区别。

你能帮我吗?

感谢

PS:重现我的应用程序,只需在Xcode中创建一个新的单一视图的应用程序,创建此代码的新文件,并设置视图控制器的视图作为ConstellationView,具有深色背景。也可以在Interface Builder或代码中将animated属性设置为true。

PPS:这是斯威夫特3,但我认为它仍然可以理解:-)

回答

1

你真的很近,只有两两件事要做!

首先,您需要在将动画添加到图层时指定键。

star.add(appearAnimation, forKey: "opacity") 

二,动画的填充模式必须kCAFillModeBackwards,而不是kCAFillModeForwards

对于更详细的参考看看 - https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/AdvancedAnimationTricks/AdvancedAnimationTricks.html

这里还有一个有趣的教程 - https://www.raywenderlich.com/102590/how-to-create-a-complex-loading-animation-in-swift

希望这有助于

全码(与CAAnimations做法!):

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) 
    } 
} 
+0

接得好 !技巧是'kCAFillModeBackwards'。动画键在这里不起任何作用,因为它只是用来在动画开始后识别动画(如果你想在后面“捕捉”它)。 –

+0

等待4小时才能奖赏赏金... –