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

1

7

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.

Shimmer answered 8/6, 2013 at 10:44 Comment(0)
P
14

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!

Persona answered 8/6, 2013 at 20:20 Comment(2)
Hey, glad you found this useful. If it answers your original question, then please mark it as the correct answer and ask a new question about the UIImage since it is a completely different technique!Persona
Just create a second ball just like you did the first one (by creating TennisBall *ball2 = ... and add it as a subview).Persona

© 2022 - 2024 — McMap. All rights reserved.