2012/02/08

Shake animation using CAKeyFrameAnimation

Animation that shakes a toolbar (UIToolbar or any generic UIView object)

I put this here so I have a quick and simple example to refer to in the future :)

- (void)shakeToolbar
{
    static BOOL performingAnimation = NO;
    if (!performingAnimation) {
        performingAnimation = YES;

        // Make an image view to animate
        // Probably not necessary when animation only "position"
        // property, but if animating its size, transform or some
        // property that will need its subviews to layout is more
        // efficient to do it like this.
        UIGraphicsBeginImageContext(toolbar.frame.size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        [self.view.layer renderInContext:context];
        UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        UIImageView *imageToAnimate;
        imageToAnimate = [[UIImageView alloc] initWithImage:img];
        imageToAnimate.frame = toolbar.frame;
        
        // Replace the toolbar with the image view temporarily
        [toolbar.superview addSubview:imageToAnimate];
        toolbar.hidden = YES;
        [imageToAnimate release];

        // Build the animation
        CGPoint pos = toolbar.layer.position;
        CGMutablePathRef shakePath = CGPathCreateMutable();
        CGPathMoveToPoint(shakePath, NULL, pos.x, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 5, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x + 4, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 3, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x + 2, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 1, pos.y);
        CGPathCloseSubpath(shakePath);
        const float durationOfShake = 0.5;
        CAKeyframeAnimation *shakeAni;
        shakeAni = [CAKeyframeAnimation animationWithKeyPath:
                                                        @"position"];
        shakeAni.path = shakePath;
        shakeAni.duration = durationOfShake;
        CFRelease(shakePath);

        // Perform the animation
        [imageToAnimate.layer addAnimation:shakeAni 
                                    forKey:@"shakeAnimation"];

        // Restore the toolbar to its original state when finished
        // (Probably is better to do this in the delegate method ... )
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 
                                      durationOfShake * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            toolbar.hidden = NO;
            [imageToAnimate removeFromSuperview];
            performingAnimation = NO;
        });
    }
}

0 comments :