对于循环覆盖动画

时间:2016-03-29 23:35:08

标签: swift core-animation

我试图在屏幕上每秒创建一个新的动画线。每一秒我都会得到一条新线,但它会覆盖旧线。我不知道为什么,但它可能是我忽略的愚蠢行为。这是我的代码:

func repeatThis() {
    for x in 1...10 {
        let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), Int64(x) * Int64(NSEC_PER_SEC))
        dispatch_after(time, dispatch_get_main_queue()) {
            var topLinePatha: UIBezierPath {
                return UIBezierPath(rect: CGRect(x: 0, y: 0 + (x * 10), width: 1, height: 10))
            }

            var topLinePathb: UIBezierPath {
                return UIBezierPath(rect: CGRect(x: 0, y: 0 + (x * 10), width: Int(UIScreen.mainScreen().bounds.width), height: 10))
            }

            let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
            expAnimation.fromValue = topLinePatha.CGPath
            expAnimation.toValue = topLinePathb.CGPath
            expAnimation.duration = self.animationTime
            expAnimation.fillMode = kCAFillModeForwards
            expAnimation.removedOnCompletion = false
            self.addAnimation(expAnimation, forKey: nil)
            print(x)
        }
    }
}

感谢您的帮助

编辑1:

这是我在动画时机中遇到的问题,基本上动画会互相覆盖:

func repeatThis() {
    var runningPath = UIBezierPath()

    for x in 0...10 {
        delay(Double(x) / 10) {
            let topLineStartPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: 1, height: 10))
            let topLineEndPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: Int(self.bounds.width), height: 10))

            let fullStartPath = runningPath.copy() as! UIBezierPath
            fullStartPath.appendPath(topLineStartPath)
            let fullEndPath = runningPath.copy() as! UIBezierPath
            fullEndPath.appendPath(topLineEndPath)

            let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
            expAnimation.fromValue = fullStartPath.CGPath
            expAnimation.toValue = fullEndPath.CGPath
            expAnimation.duration = self.animationTime
            expAnimation.fillMode = kCAFillModeForwards
            expAnimation.removedOnCompletion = false
            self.addAnimation(expAnimation, forKey: nil)
            print(x)

            runningPath = fullEndPath
        }
    }
}

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

1 个答案:

答案 0 :(得分:1)

每次动画时,您的代码都会替换CAShapeLayer的路径,因此对于您设置动画的每个“线”,都会丢失过去的行。

要显示多行,您可以:

  1. 使用方法UIBezierPath.appendPathCAShapeLayer的路径添加多个子路径,每个路径一个。
  2. 使用多个CAShapeLayer s,每行一个。
  3. 这是备选方案#1,与您当前的代码相比变化较小。这是一个自包含的示例,您可以在名为ViewController的视图控制器中添加到新的iOS项目中。

    import UIKit
    
    class MyShapeLayer: CAShapeLayer {
        var animationTime: NSTimeInterval = 0.75
    
        func repeatThis() {
            // Keep track of the path so far
            var runningPath = UIBezierPath()
    
            for x in 1...10 {
                let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), Int64(x) * Int64(NSEC_PER_SEC))
                dispatch_after(time, dispatch_get_main_queue()) {
                    // We will add a rectangular subpath onto runningPath.
                    // It will be animated starting with:
                    let topLineStartPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: 1, height: 10))
                    // and ending with:
                    let topLineEndPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: Int(self.bounds.width), height: 10))
    
                    // Copy the running path, and add the starting and ending subpaths onto it
                    let fullStartPath = runningPath.copy() as! UIBezierPath
                    fullStartPath.appendPath(topLineStartPath)
                    let fullEndPath = runningPath.copy() as! UIBezierPath
                    fullEndPath.appendPath(topLineEndPath)
    
                    // Animate from fullStartPath to fullEndPath
                    let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
                    expAnimation.fromValue = fullStartPath.CGPath
                    expAnimation.toValue = fullEndPath.CGPath
                    expAnimation.duration = self.animationTime
                    expAnimation.fillMode = kCAFillModeForwards
                    expAnimation.removedOnCompletion = false
                    self.addAnimation(expAnimation, forKey: nil)
                    print(x)
    
                    // The next time through the loop, add on to this iteration's ending path
                    runningPath = fullEndPath
                }
            }
        }
    }
    
    class MyView: UIView {
        override class func layerClass() -> AnyClass {
            return MyShapeLayer.self
        }
    }
    
    class ViewController: UIViewController {
        override func loadView() {
            self.view = MyView()
            self.view.backgroundColor = UIColor.whiteColor()
        }
    
        override func viewDidLoad() {
            if let myShapeLayer = self.view.layer as? MyShapeLayer {
                myShapeLayer.repeatThis()
            }
        }
    }
    

    结果:

    Animated GIF of the resulting animation

    这是一种替代#2的方法。我使animationTime更长,所以你可以看到每行的动画可以重叠。

    class LineAtATimeView: UIView {
        var animationTime = 1.25    // longer duration so line animations overlap
    
        func repeatAddingLines() {
            for x in 1...10 {
                let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), Int64(x) * Int64(NSEC_PER_SEC))
                dispatch_after(time, dispatch_get_main_queue()) {
                    let newLayer = CAShapeLayer()
                    newLayer.frame = self.bounds
                    self.layer.addSublayer(newLayer)
    
                    let topLineStartPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: 1, height: 10))
                    let topLineEndPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: Int(self.bounds.width), height: 10))
    
                    let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
                    expAnimation.fromValue = topLineStartPath.CGPath
                    expAnimation.toValue = topLineEndPath.CGPath
                    expAnimation.duration = self.animationTime
                    expAnimation.fillMode = kCAFillModeForwards
                    expAnimation.removedOnCompletion = false
                    newLayer.addAnimation(expAnimation, forKey: nil)
                }
            }
        }
    }
    
    class ViewController2: UIViewController {
        override func loadView() {
            self.view = LineAtATimeView()
            self.view.backgroundColor = UIColor.whiteColor()
        }
    
        override func viewDidLoad() {
            if let v = self.view as? LineAtATimeView {
                v.repeatAddingLines()
            }
        }
    }