Chain of animation of different layers

Hi everyone,

I’m trying to pars an SVG file that represent an Kanji (japanese ideogram) using the framework SVGKit.

parsing the file I’m able to get path and create for each path an CAShapeLayer. I’m able to animate them but also If the animation is in loop for the animation start for every layer at the same time. The same thing occurs when I put every layer in array and I make an loop for, out from the main for. I don’t know why. I was looking for a solution but no ones work.

I want do animate only the first level and, when finish, start the animation of the second level and so on.

like a chain.

Anyone can give me a solution?

this is code
If you need the SVG file you can download from KanjiVG Site
please help me! I am very close to the goal I want to achieve but I miss this last thing.

Thank you very much

SVGKImage* newImage = [SVGKImage imageNamed:@"kanji.svg"];
SVGKLayeredImageView *imageview = [[SVGKLayeredImageView alloc]initWithSVGKImage:newImage];

//[self.view addSubview:[[SVGKLayeredImageView alloc]initWithSVGKImage:newImage]];
//NSDictionary *dict = [newImage dictionaryOfLayers];
NSMutableArray *listLevel = [NSMutableArray new];
CGMutablePathRef combinedPath = CGPathCreateMutable();
NSString* svgTag = @"svg";
NodeList* result = [newImage.DOMDocument getElementsByTagName:svgTag];

for(SVGSVGElement* domElement in result ){
    NSString *groupTag = @"g";
    NodeList *groupElements = [domElement getElementsByTagName:groupTag];
    
    for (SVGGElement *svggElement in groupElements) {
        NSString *pathTag = @"path";
        NodeList *pathElements = [svggElement getElementsByTagName:pathTag];
        
        for (SVGPathElement *path in pathElements) {
            CALayer* layer = [newImage newCopyPositionedAbsoluteLayerWithIdentifier:path.identifier];
            if( [layer isKindOfClass:[CAShapeLayer class]] ) // should ALWAYS be true, but just in case you write your code wrong...
            {
                
                CAShapeLayer* shapeLayer = (CAShapeLayer*) layer;
                shapeLayer.strokeColor = [UIColor redColor].CGColor;
                shapeLayer.lineWidth = 1;
                CGPathAddPath(combinedPath, NULL, shapeLayer.path);
                [listLevel addObject:shapeLayer];
                //shapeLayer.path =combinedPath;
                /*
                
                layerShape.path=combinedPath;
                [CATransaction begin];
                CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
                pathAnimation.duration = 1.5f;
                pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
                pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
                pathAnimation.repeatCount = 1;
                pathAnimation.autoreverses = NO;
                [layerShape addAnimation:pathAnimation forKey:@"strokeEnd"];
                [self.view.layer addSublayer: layerShape];
                [CATransaction commit];
                */
                
                /*
                //setup the CAShapeLayer
                myShapeLayer.strokeColor = [[UIColor blueColor] CGColor];
                myShapeLayer.lineWidth = 5;
                myShapeLayer.fillColor = [[UIColor yellowColor] CGColor];
                
                
                
                //Display it!
                [self.view.layer addSublayer:myShapeLayer];
                
                //Animate path
                CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
                pathAnimation.duration = 1.5f;
                pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
                pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
                pathAnimation.repeatCount = 10;
                pathAnimation.autoreverses = YES;
                [myShapeLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
                
                //Animate colorFill
                CABasicAnimation *fillColorAnimation = [CABasicAnimation animationWithKeyPath:@"fillColor"];
                fillColorAnimation.duration = 1.5f;
                fillColorAnimation.fromValue = (id)[[UIColor clearColor] CGColor];
                fillColorAnimation.toValue = (id)[[UIColor yellowColor] CGColor];
                fillColorAnimation.repeatCount = 10;
                fillColorAnimation.autoreverses = YES;
                [myShapeLayer addAnimation:fillColorAnimation forKey:@"fillColor"];
                
                */
                
            }
        }
    }
    
    NSLog(@"nantoka");
}
/*
CGFloat timeOffset = 0;
//[CATransaction begin];
for(CAShapeLayer *layer in listLevel)
{
    CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    a.fromValue = [NSNumber numberWithFloat:0.0f];
    a.toValue = [NSNumber numberWithFloat:1.0f];
    a.repeatCount = 1;
    a.autoreverses = NO;
    a.beginTime = CACurrentMediaTime() +timeOffset;
    a.duration = 1.5f;
    a.removedOnCompletion = NO;
    [layer addAnimation:a forKey:@"Animation"];
    [self.view.layer addSublayer:layer];
    timeOffset += 1.5f;
    
}
 */
//[CATransaction commit];

CAShapeLayer *myShapeLayer = [listLevel objectAtIndex:0];
[CATransaction begin];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 1.5f;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
pathAnimation.beginTime = 0;
pathAnimation.repeatCount = 1;
pathAnimation.autoreverses = NO;
[myShapeLayer addAnimation:pathAnimation forKey:@"strokeAnimation"];
[self.view.layer addSublayer:myShapeLayer];
[CATransaction commit];
[CATransaction begin];
CAShapeLayer *myShapeLayerbis = [listLevel objectAtIndex:1];
CABasicAnimation *pathAnimationbis = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimationbis.duration = 1.5f;
pathAnimationbis.beginTime = 5.5f;
pathAnimationbis.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimationbis.toValue = [NSNumber numberWithFloat:1.0f];
pathAnimationbis.repeatCount = 1;
pathAnimationbis.autoreverses = NO;
[myShapeLayerbis addAnimation:pathAnimationbis forKey:@"strokeAnimationBis"];
[self.view.layer addSublayer:myShapeLayerbis];
[CATransaction commit];

UPDATE!!!

Hey guys!! I made a step forward. Now every stroke be drawn one after there other but there is two problem:

One: I don’t know why I find more levels than I need. For example: the kanji has 15 stroke, I found also 60 levels.

Two: All levels appears and when they leave the animations each stroke in turn disappears and then do the animation… Why?

This is code I’m using. Please! Can you help me? Thanks

  SVGKImage* newImage = [SVGKImage imageNamed:@"kanji.svg"];
   //SVGKLayeredImageView *imageview = [[SVGKLayeredImageView alloc]initWithSVGKImage:newImage]; 
    //[self.view addSubview:[[SVGKLayeredImageView alloc]initWithSVGKImage:newImage]];
    //NSDictionary *dict = [newImage dictionaryOfLayers];
    NSMutableArray *listLevel = [NSMutableArray new];
    NSString* svgTag = @"svg";
    NodeList* result = [newImage.DOMDocument getElementsByTagName:svgTag];
    for(SVGSVGElement* domElement in result ){
        NSString *groupTag = @"g";
        NodeList *groupElements = [domElement getElementsByTagName:groupTag];
        for (SVGGElement *svggElement in groupElements) {
            NSString *pathTag = @"path";
            NodeList *pathElements = [svggElement getElementsByTagName:pathTag];
            //int i = 0; float delay = 1.5;
for (SVGPathElement *path in pathElements) {
                CALayer* layer = [newImage newCopyPositionedAbsoluteLayerWithIdentifier:path.identifier];
                if( [layer isKindOfClass:[CAShapeLayer class]] ) // should ALWAYS be true, but just in case you write your code wrong...
                {
                    CAShapeLayer* shapeLayer = (CAShapeLayer*) layer;
                    //shapeLayer.strokeColor = [UIColor clearColor].CGColor;
                    shapeLayer.lineWidth = 2;
                    [listLevel addObject:shapeLayer];
                    /*
                    [CATransaction begin];
                    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
                    pathAnimation.duration = 1.5f;
                    pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
                    pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
                    pathAnimation.repeatCount = 1;
                    pathAnimation.autoreverses = NO;
                    float baseTime = [shapeLayer convertTime:CACurrentMediaTime() fromLayer:nil];
                    pathAnimation.beginTime = baseTime + (delay * i++);
                    NSLog(@"i:%d",i);
                    NSLog(@"baseTime:%f",baseTime);
                    NSLog(@"beginTime:%f",pathAnimation.beginTime);
                    
                    [shapeLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
                    [self.view.layer addSublayer:shapeLayer];
                    [CATransaction commit];
                    if (i == 15) {
                        return;
                    }
                    */
                }
            }
        }
        
        NSLog(@"nantoka");
    }
    float delay = 1.0f;
    for (int i = 0; i<15; i++) {
        CAShapeLayer* shapeLayer = [listLevel objectAtIndex:i];
        shapeLayer.strokeColor = [UIColor redColor].CGColor;
        
        [CATransaction begin];
        CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        pathAnimation.duration = 1.0f;
        pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
        pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
        pathAnimation.repeatCount = 1;
        pathAnimation.autoreverses = NO;
        float baseTime = [shapeLayer convertTime:CACurrentMediaTime() fromLayer:nil];
        pathAnimation.beginTime = baseTime + (delay * i);
        NSLog(@"i:%d",i);
        NSLog(@"baseTime:%f",baseTime);
        NSLog(@"beginTime:%f",pathAnimation.beginTime);
        
        [shapeLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
        [CATransaction commit];
        [self.view.layer addSublayer:shapeLayer];
    }`

HI rufy - I got here from the other thread where we were talking. Was just wondering, based on your description, are you correctly separating out the nested elements? If I look at 0998.svg I see that the kanji is composed of multiple sub-kanji with position given as top/bottom/right etc.

You might or might not be able to dive into the SVG library and see just what is the “level” of the path you are extracting…

The other thing I can’t see is where do you actually use the path coming from the kanji SVG? Could you be misinterpreting what is being sent? A typical path looks like MOVE, CURVE, CURVE - could you be interpreting that sequence as two or even three paths?

Hi aeberbach,

I don’t know answer at this question. Because the documentation is poor and don’t explain very well. In fact, if I put some breakpoint in the code and I look what there is in every object, I find something of strange. For example I’m trying to parse 08cea.svg and the kanji has 15 stroke but I don’t know why the parser find the 15 stroke’s path and they are from 0 to 14 index of array. but found other path for a total of 60 paths. Why? After that, repeat, the code show every path and stroke by stroke the level begin animation.

the code is this:

`for (SVGPathElement path in pathElements) {
according to the documentation with newCopyPositionedAbsoluteLayerWithIdentifier method you can transform the SVGPathElement in layer and use Core Animation to make something.
CALayer
layer = [newImage newCopyPositionedAbsoluteLayerWithIdentifier:path.identifier];
if( [layer isKindOfClass:[CAShapeLayer class]] ) // should ALWAYS be true, but just in case you write your code wrong…
{
CAShapeLayer* shapeLayer = (CAShapeLayer*) layer;
//shapeLayer.strokeColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 2;
[listLevel addObject:shapeLayer];
/*
[CATransaction begin];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@“strokeEnd”];
pathAnimation.duration = 1.5f;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
pathAnimation.repeatCount = 1;
pathAnimation.autoreverses = NO;
float baseTime = [shapeLayer convertTime:CACurrentMediaTime() fromLayer:nil];
pathAnimation.beginTime = baseTime + (delay * i++);
NSLog(@“i:%d”,i);
NSLog(@“baseTime:%f”,baseTime);
NSLog(@“beginTime:%f”,pathAnimation.beginTime);

                [shapeLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
                [self.view.layer addSublayer:shapeLayer];
                [CATransaction commit];
                if (i == 15) {
                    return;
                }
                */
            }`

Maybe. but i don’t know

maybe, but I don’t know.

I’m trying to get by with what I have and, I repeat, the documentation is really poor and unfortunately, those who work in this framework don’t give the full explanation and thinks that the documentation is fine as it is.

I hope through this forum will get to reach the goal or that someone put on this site a nice tutorial that I can be useful. I hope so.