Question

I have a CAKeyframeAnimation that I want to keep playing as I swipe my finger from the left side of the screen (the swipe-back gesture in iOS 7). Currently, it jumps to its startPosition as I swipe back and it stays static, and it looks rather sloppy.

The ViewController is a detailViewController that I have pushed from another ViewController. Of course, I don't want the animation to play after I've dismissed the detailViewController in the background. Just while it is being dismissed.

This is my animation:

CGPoint startPoint = (CGPoint){160.f, 160.f};
CGPoint endPoint = (CGPoint){160.f, 15.f};

CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath, NULL, startPoint.x, startPoint.y);
CGPathAddLineToPoint(thePath, NULL, endPoint.x, endPoint.y);

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.duration = 20.f;
animation.path = thePath;
animation.autoreverses = YES;
animation.repeatCount = INFINITY;
[self.myButton.layer addAnimation:animation forKey:@"position"];

Any help would be much appreciated, thanks!

EDIT with mode details: I have a UIView on top of a TableView (I'm not using Storyboards by the way). Inside this UIView is a UIButton that moves up and down. The UIView therefore acts as a sort of 'window' into seeing the UIButton move up and down. As I swipe back, and as the detailViewController moves rightwards, I would like the UIButton to continue animating as it would. (By on top of a tableView, I mean that it is positioned above a tableView. It is not layered on top of it).

Was it helpful?

Solution

CAAnimations only update your button's presentation layer, not it's real position. I'm not aware of any way to ask them continue to during the navigation controller's pop transition.

Instead, I'd use an NSTimer or CADisplayLink to animate the actual button position. A simplistic example:

// in MyViewController.m

static CGFloat kButtonSpeed = 0.2f;
static CGFloat kButtonMinY = 15.f;
static CGFloat kButtonMaxY = 160.f;

- (void)initiateButtonAnimation
{
    if (_displayLink) {
        return;
    }

    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(moveButton)];
    [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void)moveButton
{
    if (_frameTimestamp == 0) {
        _frameTimestamp = [_displayLink timestamp];
        return;
    }

    if (!_renderFrame) {
        _renderFrame = !_renderFrame;
        return;
    }

    double currentTime = [_displayLink timestamp];
    double renderTime = currentTime - _frameTimestamp;
    _frameTimestamp = currentTime;
    CGFloat amount = renderTime * 60.f * kButtonSpeed;

    CGFloat y = self.myButton.center.y;

    if (!_moveButtonDown && y > kButtonMinY) {
        y = y - amount;
    }
    else if (_moveButtonDown && y < kButtonMaxY)
        y = y + amount;
    else
        _moveButtonDown = !_moveButtonDown;

    self.myButton.center = CGPointMake(self.myButton.center.x, y);
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];

    // don't forget to invalidate & release
    [_displayLink invalidate];
    _displayLink = nil;
}

OTHER TIPS

UIButton* button = [[UIButton alloc]initWithFrame:CGRectMake(160, 0, 320, 540)];
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction animations:^{
    button.frame = CGRectMake(160, 10, 320, 540);
} completion:^(BOOL finished) {
    // nothing to see here
}];

This is probably the simplest way to accomplish the animation. You might have to fiddle with different animation options, but this is the gist of what you want.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top