2016-09-21 1 views
0

Ich möchte eine benutzerdefinierte UIView-Unterklasse erstellen, die eine Reihe von Sternen an einem dunkelblauen Himmel darstellt.Benutzerdefinierte UIView: animiere SubLayers mit Verzögerung

Deshalb habe ich diese Ansicht:

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

} 

Es funktioniert ganz gut, hier ist das Ergebnis: enter image description here

aber ich möchte es animiert haben, das heißt, jeder der 80 Sterne sollte nacheinander mit einer Verzögerung von 1 Sekunde erscheinen.

Ich habe versucht, die beginTime meiner Animation zu erhöhen, aber es scheint nicht den Trick zu tun. Ich habe mit drawRect oder layoutSubviews überprüft, aber es gibt keinen Unterschied.

Können Sie mir helfen?

Dank

PS: meine App reproduzieren, erstellen Sie einfach eine neue Single View-App in XCode, eine neue Datei mit diesem Code erstellen, und legen Sie die Ansicht des Viewcontroller als ConstellationView, mit einem dunklen Hintergrundfarbe. Setzen Sie die Eigenschaft animated auch auf true, entweder im Interface Builder oder im Code.

PPS: Das ist in Swift 3, aber ich denke, es

Antwort

1

Du bist immer noch verständlich :-) ist wirklich nahe, nur zwei Dinge zu tun!

Zuerst müssen Sie den Schlüssel angeben, wenn Sie die Animation zum Layer hinzufügen.

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

Zweitens, die Füllmodus für die Animation benötigt kCAFillModeBackwards statt kCAFillModeForwards zu sein.

Für eine detailliertere Referenz Siehe - https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/AdvancedAnimationTricks/AdvancedAnimationTricks.html

Und hier ist ein Spaß-Tutorial - https://www.raywenderlich.com/102590/how-to-create-a-complex-loading-animation-in-swift

this helps

Voll-Code (für die Praxis mit 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

Guter Fang ! Der Trick war 'kCAFillModeBackwards'. Der Animationsschlüssel spielt hier keine Rolle, da er nur dazu dient, eine Animation nach dem Start zu identifizieren (wenn Sie sie später "fangen" wollen). –

+0

Warten 4 Stunden, um das Kopfgeld zu vergeben ... –

Verwandte Themen