Elastic dragging with a CALayer

You’ll notice when you pull beyond the bounds of a UITableView that the table view appears spring loaded before jumping back when you lift your finger off the device. I posted a project to GitHub that illustrates something similar using a CALayer and the UITouch event handlers on UIView.

There’s a few strange things I did using a category on UIWindow to handle the touch events, but you could implement the same functionality on any UIView. The important things are the touch callbacks which are posted below.

static BOOL tapOnImageLayer;
static CGPoint initialPosition;
static float velocity = 900; // px / sec

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (tapOnImageLayer)
    {
        CALayer *imageLayer = [(elasticAppDelegate_iPhone *)[UIApplication sharedApplication].delegate imageLayer];
        
        CGFloat distance = CGPointDistance(initialPosition, imageLayer.position);
        
        NSTimeInterval time = distance / velocity;
        NSLog(@"%f %f", distance, time);
        [CATransaction begin];
        [CATransaction setDisableActions:NO];
        [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [CATransaction setAnimationDuration:time];
        
        imageLayer.position = initialPosition;
        
        [CATransaction commit];
        
        tapOnImageLayer = NO;
    }
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *t = [touches anyObject];
    CALayer *layer = [self.layer hitTest:[t locationInView:self]];
    if (layer == [(elasticAppDelegate_iPhone *)[UIApplication sharedApplication].delegate imageLayer])
    {
        tapOnImageLayer = YES;
        initialPosition = layer.position;

    }
    else
    {
        tapOnImageLayer = NO;
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    if (tapOnImageLayer)
    {
        CALayer *imageLayer = [(elasticAppDelegate_iPhone *)[UIApplication sharedApplication].delegate imageLayer];
        
        CGSize delta = CGSizeMake([touch locationInView:self.window].x - initialPosition.x, 
                                  [touch locationInView:self.window].y - initialPosition.y);
        
        [CATransaction begin];
        [CATransaction setDisableActions:YES];
        
        imageLayer.position = CGPointOffset(initialPosition, CGSizeMultiplyScalar(delta, 0.7));
        
        [CATransaction commit];
    }
}

The important parts are setting the desired spring back velocity in line 3, and scaling the drag position in line 56. There are a couple of convenience functions for working with the CGFoundation types:

CGPoint CGPointOffset (CGPoint point1, CGSize delta)
{
    CGPoint p;
    p.x = point1.x + delta.width;
    p.y = point1.y + delta.height;
    
    return p;
}

CGFloat CGPointDistance (CGPoint p1, CGPoint p2)
{
    CGFloat dx = p1.x - p2.x;
    CGFloat dy = p1.y - p2.y;
    
    return sqrtf(dx * dx + dy * dy);
}

CGSize CGSizeMultiplyScalar (CGSize p, CGFloat scalar)
{
    return CGSizeMake(p.width * scalar, p.height * scalar);
}

I’ll post this and the other code I’ve done on Github shortly (I’m pretty new to it and still trying to figure it out!)

Advertisements

Posted on March 7, 2011, in Code and tagged , , , . Bookmark the permalink. 2 Comments.

  1. Hey, I was just doing some perusing around the internet looking for new things to pick up while learning iOS. I found your blog really helpful.

    I was hoping I could ask you to send me a copy of the Xcode project you had for this. I’m new to CAAnimations and I feel like I’m not quite getting the whole scope with just that code there. I found your repository on github, but it was empty.

    Thanks for your time,

  2. TJ, unfortunately I lost the code on a stolen computer. Most of the important bits are there, however, and you can hover over the code frame and copy it as plain text.

    Best,
    –pz–

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: