small medium large xlarge

Profilbild_bernd_rabe_pragsmall
09 Jul 2011, 08:51
Bernd Rabe (23 posts)

Hey Bill, community,

it takes a couple of code lines to animate 2 images (each covering half of a iPhone screen) as some to the top/bottom, let them stay there for x seconds (give the use the chance to read something on the view behind), move the back into the original position, let them stay there for x seconds and them remove them altogether.

I’m talking about a UIImageView modifying position ( position.y +/- image view height) and shadowOpacity (between 0 -> 1/1 -> 0).

My first idea was to use the action dictionary to let the layer pick a basic animation and only modifying the duration and timing function during layer.shadowOpacity = 1. or layer.position = CGPointMake(layer.position.x, layer.position.y +/- value).

Here’s what I found out.

  1. simply modifying a UIView backed layer property does change the value immediatly without animation, so you must enclose it in [UIView beginAnimations…]/[UIView commitAnimations] to see an animation.
  2. if you do so it always picks up the default animation but not the animations defined in the actions dictionary

So I defined MyImageView class, didn’t use the actions dictionary but over wrote the actionForLayer:forKey: method. Looks as follows.

`
- (id)actionForLayer:(CALayer *)theLayer forKey:(NSString *)theKey
{
    CABasicAnimation *theAnimation = nil;
 
    if ([theKey isEqualToString:@"shadowOpacity"] && theLayer.name != nil) {
        DLog(@"%@ (name = %@) - %@", theLayer, theLayer.name, theKey);
        theAnimation = [CABasicAnimation animation];
        theAnimation.toValue = [NSNumber numberWithFloat:theLayer.shadowOpacity];
        theAnimation.fromValue = [NSNumber numberWithFloat:theLayer.shadowOpacity == 0. ? 1. : 0.];
        theAnimation.duration = 5.;
        theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    } else {
        if ([theKey isEqualToString:@"position"] && theLayer.name != nil) {
            CGPoint point = theLayer.position;
            CGFloat delta = theLayer.bounds.size.height / 2;
            theAnimation = [CABasicAnimation animation];
            theAnimation.duration = 5.;
            theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
            theAnimation.toValue = [NSValue valueWithCGPoint:theLayer.position];

            if ([theLayer.name isEqualToString:@"firstImageViewLayer"]) {
                DLog(@"%@ (name = %@) - %@", theLayer, theLayer.name, theKey);
                point.y = point.y < 0 ? point.y + delta : point.y - delta;
                theAnimation.fromValue = [NSValue valueWithCGPoint:point];
            }
            else {
                if ([theLayer.name isEqualToString:@"secondImageViewLayer"]) {
                    DLog(@"%@ (name = %@) - %@", theLayer, theLayer.name, theKey);
                    point.y = point.y < theLayer.bounds.size.height ? point.y + delta : point.y - delta;
                    theAnimation.fromValue = [NSValue valueWithCGPoint:point];
                }
            }
        }
    }
    return theAnimation;
}
`

But that doesn't work either as expected. The action trigger occurs but the value you set (layer.opacity = 1.) are not present in theLayer.shadowOpacity so the 
returned animations are not the ones you'd expect.

If anyone found a way to use setter methods to trigger an animation instead of 
[UIView animateWithDuration:animations:completion:[UIView animateWithDuration:animations:completion:]] please give me a hint.

P.S. The behavior on the mac seems to be quite different as the AnimationAndAction xcode project from Bill's book shows.


.... or am I completely wrong

You must be logged in to comment