Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create smooth animation with UIImageView animation

I'm currently animating between 4 images like this:

UIImageView *tom3BeforeImage;
tom3Images = [NSArray arrayWithObjects: [UIImage imageNamed:@"floortom_before1.png"],[UIImage imageNamed:@"floortom_before2.png"],[UIImage imageNamed:@"floortom_before3.png"],[UIImage imageNamed:@"floortom_before4.png"], nil ];
tom3BeforeImage.animationImages = tom3Images;
tom3BeforeImage.animationDuration = 0.75;
[tom3BeforeImage startAnimating];

It works fine, except that the animation is choppy between the images. I need the duration to be exactly .75 seconds, so speeding it up is not an option.

What's the best way to have the animation be smoother between the images, kind of like blending between each image change?

Thanks!

like image 297
codeman Avatar asked Jan 11 '23 10:01

codeman


1 Answers

If you're using frame based UIImageView animation, and the animation must be .75 seconds, then the only way I know of to make it smoother is to create more frames. Try 30 frames/second, or about 22 frames. That should give very smooth motion.

If you want some sort of cross-dissolve between frames then you won't be able to use UIView frame animation. you'll have to use UIView block animation (using animateWithDuration:animations: or its cousins.)

You could create a sequence of cross-dissolves between your frames where the total duration of the sequence is .75 seconds. Have each transition trigger the next transition in it's completion block.

Something like this:

You'll need 2 image views, stacked on top of each other. You'll fade one out and the other in at the same time. You'll need to set the opaque flag to NO on both.

Lets call them tom3BeforeImage1 and tom3BeforeImage2

Add an int instance variable imageCount and make your array of images, tom3Images, and instance variable as well:

- (void) animateImages;
{
  CGFloat duration = .75 / ([tom3Images count] -1);

  //Start with the current image fully visible in tom3BeforeImage1
  tom3BeforeImage1.image = tom3Images[imageCount];
  tom3BeforeImage1.alpha = 1.0;

  //Get the next image ready, at alpha 0, in tom3BeforeImage2
  tom3BeforeImage2.image = tom3Images[imageCount+1];
  tom3BeforeImage2.alpha = 0;
  imageCount++

  [UIView animateWithDuration: duration  
    delay: 0 
    options: UIViewAnimationOptionCurveLinear
    animations:
    ^{
      //Fade out the current image
       tom3BeforeImage1.alpha = 0.0;

       //Fade in the new image
       tom3BeforeImage2.alpha = 1.0;
    }
    completion:
    ^{
    //When the current animation step completes, trigger the method again.
    if (imageCount < [tom3Images count] -1)
       [self animateImages];
    }
  ];
}

Note that I banged out the code above in the forum editor without having had enough coffee. It likely contains syntax errors, and may even have logic problems. This is just to get you thinking about how to do it.

Edit #2:

I'm not sure why, but I decided to flesh this out into a full-blown example project. The code above works passably well after debugging, but since it's fading one image out at the same time it's fading another one in, the background behind both image views shows through.

I reworked it to have logic that only fades the top image in and out. It puts the first frame in the top image view and the second frame in the bottom image view, then fades out the top image view.

The project is up on github, called Animate-Img. (link)

Then it installs the third frame in the top image view and fades it IN,

Then it installs the 4th fame in the bottom image view and fades out the top to expose the bottom, etc, etc.

I ended up creating a generalized method

- (void) animateImagesWithDuration: (CGFloat) totalDuration
                           reverse: (BOOL) reverse
                         crossfade: (BOOL) doCrossfade
               withCompletionBlock: (void (^)(void)) completionBlock;

It will animate a set of images, into a pair of image views, optionally reversing the animation once it's done. It takes a completion block that gets called once the animation is finished.

The animate button actually calls a method that repeats the whole animation sequence. It's currently set to only run it once, but changing a constant will make the program repeat the whole sequence, if desired.

like image 56
Duncan C Avatar answered Jan 18 '23 20:01

Duncan C