Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing animation

I'm creating a simple app where when the user presses a button, a series of lines will be drawn on the screen and the user will be able to see these lines drawn in real time (almost like an animation).

My code looks something like this (has been simplified):

UIGraphicsBeginImageContext(CGSizeMake(300,300));
CGContextRef context = UIGraphicsGetCurrentContext();

for (int i = 0; i < 100; i++ ) {
    CGContextMoveToPoint(context, i, i);
    CGContextAddLineToPoint(context, i+20, i+20);
    CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
    CGContextStrokePath(context);
}

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

My problem is that:

1) As soon as the user presses the button, the UIThread blocks until the drawing is done.

2) I can't get the lines to be drawn on the screen one at a time - I've tried setting the UIImage directly inside the loop and also tried setting a layer content inside the loop.

How do I get around these problems?

like image 889
HHHH Avatar asked Oct 09 '12 20:10

HHHH


1 Answers

You say "just like an animation". Why not do an actual animation, a la Core Graphics' CABasicAnimation? Do you really need to show it as a series of lines, or is a proper animation ok?

If you want to animate the actual drawing of the line, you could do something like:

#import <QuartzCore/QuartzCore.h>

- (void)drawBezierAnimate:(BOOL)animate
{
    UIBezierPath *bezierPath = [self bezierPath];

    CAShapeLayer *bezier = [[CAShapeLayer alloc] init];

    bezier.path          = bezierPath.CGPath;
    bezier.strokeColor   = [UIColor blueColor].CGColor;
    bezier.fillColor     = [UIColor clearColor].CGColor;
    bezier.lineWidth     = 5.0;
    bezier.strokeStart   = 0.0;
    bezier.strokeEnd     = 1.0;
    [self.view.layer addSublayer:bezier];

    if (animate)
    {
        CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        animateStrokeEnd.duration  = 10.0;
        animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f];
        animateStrokeEnd.toValue   = [NSNumber numberWithFloat:1.0f];
        [bezier addAnimation:animateStrokeEnd forKey:@"strokeEndAnimation"];
    }
}

Then all you have to do is create the UIBezierPath for your line, e.g.:

- (UIBezierPath *)bezierPath
{
    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:CGPointMake(0.0, 0.0)];

    [path addLineToPoint:CGPointMake(200.0, 200.0)];

    return path;
}

If you want, you can patch a bunch of lines together into a single path, e.g. here is a roughly sine curve shaped series of lines:

- (UIBezierPath *)bezierPath
{
    UIBezierPath *path = [UIBezierPath bezierPath];
    CGPoint point = self.view.center;

    [path moveToPoint:CGPointMake(0, self.view.frame.size.height / 2.0)];

    for (CGFloat f = 0.0; f < M_PI * 2; f += 0.75)
    {
        point = CGPointMake(f / (M_PI * 2) * self.view.frame.size.width, sinf(f) * 200.0 + self.view.frame.size.height / 2.0);
        [path addLineToPoint:point];
    }

    return path;
}

And these don't block the main thread.

By the way, you'll obviously have to add the CoreGraphics.framework to your target's Build Settings under Link Binary With Libraries.

like image 114
Rob Avatar answered Oct 12 '22 23:10

Rob