2016-09-21 99 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小時才能獎賞賞金... –