Search code examples
swiftuibezierpathcashapelayercabasicanimationcgpath

How to animate the drawing of an array of CGpath?


I am trying to make an app that draws an object for the user. I now have one of those object, which is built up of an array of type [UIBezierPath]. I then use a loop to change all the UIBezierPaths in the array into CGPaths and then want to animate those paths being drawn one by one. However when I try this code now it doesn't work, and I can't really find any helpful information about this online. This is the code is use to transform the array macbookPath which consists of UIBezierPath into CGPath:

for path in macbookPath {
        drawingPath.append(path.cgPath)
    }

And then I use this code to try and draw the path:

for cgPath in drawingPath {
        shapeLayer.path = cgPath
    }

This is the rest of the code for the function drawForm() which is supposed to draw the form onto a view called aiDrawView:

@objc func drawForm() {
    var drawingPath = [CGPath]()
    var macbookPath = Forms.MacbookForm()
    let shapeLayer = CAShapeLayer()
    shapeLayer.frame = aiDrawView.bounds

    for path in macbookPath {
        drawingPath.append(path.cgPath)
    }

    for cgPath in drawingPath {
        shapeLayer.path = cgPath
    }

    let strokeEndAnimation = CABasicAnimation(keyPath: "strokeEnd")
    strokeEndAnimation.duration = 2.0
    strokeEndAnimation.fromValue = 0.0

    shapeLayer.add(strokeEndAnimation, forKey: "strokeEndAnimation")
}

I am very new to CAShapeLayer and CABasicAnimation and UIBezierPath so any help would be tremendously appreciated!!! :D


Solution

  • I'd suggest building a single UIBezierPath from your array of paths and then animate that:

    var paths: [UIBezierPath] = ...
    
    let path = UIBezierPath()
    
    paths.forEach { path.append($0) }
    
    shapeLayer.path = path.cgPath
    
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.duration = 2.0
    animation.fromValue = 0.0
    shapeLayer.add(animation, forKey: nil)
    

    E.g., here are 100 paths, combined into one path, whose strokeEnd is then animated on a shape layer:

    enter image description here

    Assuming your array of paths really form some continuous path, I might suggest storing it as an array of CGPoint instead, though. Then you can build a single path from that. That eliminates the overhead of all of this path stuff. Plus it opens up door for scaling of the original dataset without introduction of any artifacts, being able to use line join options, applying smoothing algorithms if you have undesirable sequence of line segments, etc.