Apollo
Apollo

Reputation: 9064

Logarithmic scale for animation objective-c

I'm trying to make a circle expand quickly for the first 1-2 seconds and then decrease in speed by which it grows. I thought that a logarithmic scale would be best suited for this, but I don't know how to create one. I'm using the following code to animate the circle:

// Create a view with a corner radius as the circle
self.circle = [[UIView alloc] initWithFrame:CGRectMake(currentPos.x, currentPos.y, 10, 10)];
[self.circle.layer setCornerRadius:self.circle.frame.size.width / 2];
[self.circle setBackgroundColor:[UIColor clearColor]];
self.circle.layer.borderColor = [UIColor redColor].CGColor;
self.circle.layer.borderWidth = .5f;
UIPanGestureRecognizer *move = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.circle addGestureRecognizer:move];
[self.view addSubview:self.circle];

[UIView animateWithDuration:5 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^(void){

    // Animate it to double the size
    const CGFloat scale = 2;
    [self.circle setTransform:CGAffineTransformMakeScale(scale, scale)];
} completion:nil];

Upvotes: 0

Views: 830

Answers (3)

Duncan C
Duncan C

Reputation: 131491

I don't think view animations allow any curves other than linear and the "ease" style animations.

As I recall, Core Animation allows you to define a custom timing function using a cubic bezier curve. You should be able to create a bezier curve that approximates a log curve.

See the docs on CAMediaTimingFunction for info on creating custom timing functions for Core Animation methods.

Be warned, though, that Core Animation is a pretty involved subject. Core Animation methods are not nearly as easy to use as UIView animations like the code you posted.

Upvotes: 0

pbasdf
pbasdf

Reputation: 21536

I would use animateKeyframesWithDuration:. This lets you set the scale at different points during the animation (so it can be non-linear). You do this with separate 'addKeyFrameWithRelativeStartTime:` calls. For example:

double total_duration = 5;
[UIView animateKeyframesWithDuration:total_duration delay:0 options:UIViewKeyframeAnimationOptionAllowUserInteraction animations:^(void){
    const CGFloat final_scale = 2;
    double acceleration = 1000; // the bigger this number, the bigger the initial acceleration
    double multiplier = (final_scale - 1) / (logf(1+ (1/acceleration)) - logf(1/acceleration));
    double addon = 1 - multiplier * logf(1/acceleration);
    double segments = 20;
    for (int segment = 0 ; segment < segments ; segment++) {
        [UIView addKeyframeWithRelativeStartTime:(segment/segments) relativeDuration:(1.0/segments) animations:^(void){
            double scale = multiplier * logf(segment/segments + (1/acceleration)) + addon;
            self.circle.transform = CGAffineTransformMakeScale(scale,scale);
        }];
    }
} completion:nil];

achieves roughly what you want (though forgive the messy maths, it can probably be simplified)!

Upvotes: 0

SomeGuy
SomeGuy

Reputation: 9700

The easiest way is to use the built in animation options, you can set the animation ease (UIViewAnimationOptionCurveEaseOut)

[UIView animateWithDuration:2 delay:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^(void){

    // Animate it to double the size
    const CGFloat scale = 2;
    [self.circle setTransform:CGAffineTransformMakeScale(scale, scale)];
} completion:nil];

These are the built in ease types: enter image description here

If you wanted something different to this then you'd need to do it yourself I believe.

Thanks to this post for the ease images How to create custom easing function with Core Animation?

Upvotes: 2

Related Questions