Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: CAShapeLayer to draw non-rectagular image and animating its shape

I have been reading the documentation on CAShapeLayer but I still don't quite get it. From my understanding, Layer is always flat and its size is always a rectangle.

CAShapeLayer on the other hand, allows you to define a layer that is not just rectangle-like. It can be a circle shape, triangle etc as long as you use it with UIBezierPaths.

Is my understanding off here?

What I had in mind is, for example, a tennis ball that bounces off the edges on the screen (easy enough), but I would like to show a little animation not using image animations - I would like it to show a little "squeezed" like animation as it hits the edge of the screen and then bounces off. I am not using a tennis ball image. Just a yellow color filled circle.

Am I correct here with CAShapeLayer to accomplish this? If so, can you please provide a litle example? Thanks.

like image 287
Unheilig Avatar asked Jun 08 '13 10:06

Unheilig


1 Answers

While you do use a path to define the shape of the layer, it is still created with a rectangle used to define the frame/bounds. Here is an example to get you started:

TennisBall.h:

#import <UIKit/UIKit.h>

@interface TennisBall : UIView

- (void)bounce;

@end

TennisBall.m:

#import <QuartzCore/QuartzCore.h>
#import "TennisBall.h"

@implementation TennisBall

+ (Class)layerClass
{
    return [CAShapeLayer class];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setupLayer];
    }
    return self;
}

/* Create the tennis ball */
- (void)setupLayer
{
    CAShapeLayer *layer = (CAShapeLayer *)self.layer;
    layer.strokeColor = [[UIColor blackColor] CGColor];
    layer.fillColor = [[UIColor yellowColor] CGColor];
    layer.lineWidth = 1.5;

    layer.path = [self defaultPath];
}

/* Animate the tennis ball "bouncing" off of the side of the screen */
- (void)bounce
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.duration = 0.2;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animation.fromValue = (__bridge id)[self defaultPath];
    animation.toValue = (__bridge id)[self compressedPath];
    animation.autoreverses = YES;
    [self.layer addAnimation:animation forKey:@"animatePath"];
}

/* A path representing the tennis ball in the default state */
- (CGPathRef)defaultPath
{
    return [[UIBezierPath bezierPathWithOvalInRect:self.frame] CGPath];
}

/* A path representing the tennis ball is the compressed state (during the bounce) */
- (CGPathRef)compressedPath
{
    CGRect newFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width * 0.85, self.frame.size.height);
    return [[UIBezierPath bezierPathWithOvalInRect:newFrame] CGPath];
}

@end

Now, when you want to use this:

TennisBall *ball = [[TennisBall alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
[self.view addSubview:ball];

[ball bounce];

Note that this will need to be extended so that you can "bounce" from different angles, etc. but it should get you pointed in the right direction!

like image 164
lnafziger Avatar answered Sep 28 '22 10:09

lnafziger