Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I haven't analyzed your code enough to be 100% sure why <code>[[container.layer presentationLayer] frame]</code> might not return what you expect. But I see several problems.</p> <p>One obvious problem is that <code>moveDown:</code> doesn't declare <code>movePath</code>. If <code>movePath</code> is an instance variable, you probably want to clear it or create a new instance each time <code>moveDown:</code> is called, but I don't see you doing that.</p> <p>A less obvious problem is that (judging from your use of <code>removedOnCompletion</code> and <code>fillMode</code>, in spite of your use of <code>presentationLayer</code>) you apparently don't understand how Core Animation works. This turns out to be surprisingly common, so forgive me if I'm wrong. Anyway, read on, because I will explain how Core Animation works and then how to fix your problem.</p> <p>In Core Animation, the layer object you normally work with is a <em>model layer</em>. When you attach an animation to a layer, Core Animation creates a copy of the model layer, called the <em>presentation layer</em>, and the animation changes the properties of the presentation layer over time. An animation never changes the properties of the model layer.</p> <p>When the animation ends, and (by default) is removed, the presentation layer is destroyed and the values of the model layer's properties take effect again. So the layer on screen appears to “snap back” to its original position/color/whatever.</p> <p>A common, but <strong>wrong</strong> way to fix this is to set the animation's <code>removedOnCompletion</code> to <code>NO</code> and its <code>fillMode</code> to <code>kCAFillModeForwards</code>. When you do this, the presentation layer hangs around, so there's no “snap back” on screen. The problem is that now you have the presentation layer hanging around with different values than the model layer. If you ask the model layer (or the view that owns it) for the value of the animated property, you'll get a value that's different than what's on screen. And if you try to animate the property again, the animation will probably start from the wrong place.</p> <p>To animate a layer property and make it “stick”, you need to change the model layer's property value, and then apply the animation. That way, when the animation is removed, and the presentation layer goes away, the layer on screen will look exactly the same, because the model layer has the same property values as its presentation layer had when the animation ended.</p> <p>Now, I don't know why you're using a keyframe to animate straight-line motion, or why you're using an animation group. Neither seems necessary here. And your two methods are virtually identical, so let's factor out the common code:</p> <pre><code>- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y { CGPoint fromValue = [layer.presentationLayer position]; CGPoint toValue = CGPointMake(fromValue.x, y); layer.position = toValue; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fromValue = [NSValue valueWithCGPoint:fromValue]; animation.toValue = [NSValue valueWithCGPoint:toValue]; animation.duration = 2; [layer addAnimation:animation forKey:animation.keyPath]; } </code></pre> <p>Notice that we're giving the animation a key when I add it to the layer. Since we use the same key every time, each new animation will replace (remove) the prior animation if the prior animation hasn't finished yet.</p> <p>Of course, as soon as you play with this, you'll find that if you <code>moveUp:</code> when the <code>moveDown:</code> is only half finished, the <code>moveUp:</code> animation will appear to be at half speed because it still has a duration of 2 seconds but only half as far to travel. We should really compute the duration based on the distance to be travelled:</p> <pre><code>- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY { CGPoint fromValue = [layer.presentationLayer position]; CGPoint toValue = CGPointMake(fromValue.x, y); layer.position = toValue; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fromValue = [NSValue valueWithCGPoint:fromValue]; animation.toValue = [NSValue valueWithCGPoint:toValue]; animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY); [layer addAnimation:animation forKey:animation.keyPath]; } </code></pre> <p>If you <em>really</em> need it to be a keypath animation in an animation group, your question should show us <em>why</em> you need those things. Anyway, it works with those things too:</p> <pre><code>- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY { CGPoint fromValue = [layer.presentationLayer position]; CGPoint toValue = CGPointMake(fromValue.x, y); layer.position = toValue; UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:fromValue]; [path addLineToPoint:toValue]; CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; animation.path = path.CGPath; animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY); CAAnimationGroup *group = [CAAnimationGroup animation]; group.duration = animation.duration; group.animations = @[animation]; [layer addAnimation:group forKey:animation.keyPath]; } </code></pre> <p>You can find the full code for my test program <a href="https://gist.github.com/4343026">in this gist</a>. Just create a new Single View Application project and replace the contents of <code>ViewController.m</code> with the contents of the gist.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload