I have a requirement to animate images . I have a large number of images and this needs to be played as an video. In between playing sometimes i need to change some images as they will be updated at server. so playing should automatically update this new images .
I have tried using UIImageView. There we cannot control the animation.
I then tried CAKeyframeAnimation supplying image array to values property. I could play and pause the animation. But here also i cannot dynamically change the image while playing.
Can anyone help me in solving this problem.
Thanks
mia
The animation system on UIImageView is very limited. I would suggest that you make your own implementation with say 2 image view.
You would than change the image in one imageview and the fade in and out using UIView animateWithDuration
I have written a method for you. Please note: I have not tested it.
It assumes you have your frames in a array called 'frames' and have two UIIMageView placed on top of each other called 'imgv1' and 'imgv2'
-(void)performAnimationOfFrameAtIndex:(NSNumber*)indexNum
{
int index = [indexNum intValue];
if(index >= frames.count)
{
return;
}
UIImage *img = [frames objectAtIndex:index];
UIImageView *imgIn;
UIImageView *imgOut;
if(index % 2 == 0)
{
imgIn = imgv1;
imgOut = imgv2;
}
else {
imgIn = imgv2;
imgOut = imgv1;
}
imgIn.image = img;
[self.view sendSubviewToBack:imgIn];
[UIView animateWithDuration:0.1 animations:^{
imgIn.alpha = 1;
imgOut.alpha = 0;
} completion:^(BOOL finished) {
[self performSelectorOnMainThread:#selector(performAnimationOfFrameAtIndex:) withObject:[NSNumber numberWithInt:index+1] waitUntilDone:NO];
}];
}
Use two UIImageViews and swap them.
If you have a weak reference on the "animation" UIView, be sure to check if animation has finished in the completion block. Otherwise you may experience performance issues when you navigate to another view, recursive calls to animation method will continue!
- (void)animateImagesAtIndex:(NSNumber *)imageIdx {
// Do something here
[UIView animateWithDuration:1 animations:^{
// Do swap
}
completion:^(BOOL finished) {
if (finished) {
[self animateImagesAtIndex:imageIdx];
}
}];
}
UIImageView supports animations for a series of images. You only have to set the properties animationImages with an array of the images, and call the methods startAnimating and stopAnimating.
Related
There is a strang flicker when I use AVPlay:
I create a demo to show this bug: Demo 。
I am referring to the flicker from the beginning, that one happens because I have a transition between the image and the movie. but I need
the image showing before the video appear.
because I have to keep image showing,before the video is downloaded completely.
Can anyone fix this bug?
You can make this transition smoother by using an animation when hiding the image view:
if (!self.imageView.hidden) {
NSTimeInterval fadeDuration = .5;
static CGFloat invisibleAlpha = .0;
[UIView animateWithDuration:fadeDuration animations:^{
self.imageView.alpha = invisibleAlpha;
} completion:^(BOOL finished) {
[self.player play];
}];
}
Note that you can set the .5 value to another value.
I know there's lots of 3rd party solutions out there, but I've decided to build one myself to be in full control of the animations and to learn something new.
It seems like most of the time it is made through subclassing UIControl and tracking touches, but I've used a different approach. I create system buttons with images. This way I get a nice highlight animation on a press for free. Anyway, it works quite nice until you press on it really fast. Here's a gif of what's happening.
Sometimes, if you do it really fast a full star gets stuck. I'll try to explain what's happening behind this animation. Since full and empty stars have different color, I change tint color of the button each time i becomes full or empty. The animation you see is a separate UIImageView that is added on top of the button right before the beginning of the animation block and is removed in the completion block. I believe sometimes on emptying the star animation is not fired and image is not removed in the completion block. But I can't catch this bug in code.
I added some debugging code to see if the block that makes a star empty is even fired. Seems like it is, and even completion block is called.
2015-04-23 13:00:00.416 RERatingControl[24011:787202]
Touch number: 5
2015-04-23 13:00:00.416 RERatingControl[24011:787202]
removing a star at index: 4
2015-04-23 13:00:00.693 RERatingControl[24011:787202] star removed
Star indexes are zero-based, so the 5th star is supposed to be removed, but it's not.
Full project is on github. It's pretty small. I would appreciate if someone would help me fix this. Oh, and feel free to use this code if you want to :-)
UPDATE. I was suggested to post some logic code here. I didn't do it right away because I felt it would be too cumbersome.
Button action method:
- (void)buttonPressed:(id)sender {
static int touchNumber = 0;
NSLog(#"\nTouch number: %d", touchNumber);
touchNumber++;
if (self.isUpdating) {
return;
}
self.updating = YES;
NSInteger index = [self.stars indexOfObject:sender];
__block NSTimeInterval delay = 0;
[self.stars enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
REStarButton *btn = (REStarButton*)obj;
if (idx <= index) {
if (!btn.isFull) {
NSLog(#"\nadding a star at index: %ld", (long)idx);
[btn makeFullWithDelay:delay];
delay += animationDelay;
}
} else {
if (btn.isFull) {
NSLog(#"\nremoving a star at index: %ld", (long)idx);
[btn makeEmptyWithDelay:0];
}
}
}];
if ([self.delegate respondsToSelector:#selector(ratingDidChangeTo:)]) {
[self.delegate ratingDidChangeTo:index + 1];
}
self.updating = NO;
}
Empty a full star method:
- (void)makeEmptyWithDelay:(NSTimeInterval)delay {
// if (!self.isFull) {
// return;
// }
self.full = NO;
UIImageView *fullStar = [[UIImageView alloc] initWithImage:self.starImageFull];
[self addSubview:fullStar];
CGFloat xOffset = CGRectGetMidX(self.bounds) - CGRectGetMidX(fullStar.bounds);
CGFloat yOffset = CGRectGetMidY(self.bounds) - CGRectGetMidY(fullStar.bounds);
fullStar.frame = CGRectOffset(fullStar.frame, xOffset, yOffset);
[self setImage:self.starImageEmpty forState:UIControlStateNormal];
[self setTintColor:self.superview.tintColor];
[UIView animateWithDuration:animationDuration delay:delay options:UIViewAnimationOptionCurveEaseOut animations:^{
//fullStar.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
fullStar.transform = CGAffineTransformMakeTranslation(0, 10);
fullStar.alpha = 0;
} completion:^(BOOL finished) {
[fullStar removeFromSuperview];
NSLog(#"star removed");
}];
Make an empty star full method:
}
- (void)makeFullWithDelay:(NSTimeInterval)delay {
// if (self.isFull) {
// return;
// }
self.full = YES;
UIImageView *fullStar = [[UIImageView alloc] initWithImage:self.starImageFull];
CGFloat xOffset = CGRectGetMidX(self.bounds) - CGRectGetMidX(fullStar.bounds);
CGFloat yOffset = CGRectGetMidY(self.bounds) - CGRectGetMidY(fullStar.bounds);
fullStar.frame = CGRectOffset(fullStar.frame, xOffset, yOffset);
fullStar.transform = CGAffineTransformMakeScale(0.01f, 0.01f);
[self addSubview:fullStar];
[UIView animateKeyframesWithDuration:animationDuration delay:delay options:0 animations:^{
CGFloat ratio = 0.35;
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:ratio animations:^{
fullStar.transform = CGAffineTransformMakeScale(animationScaleRatio, animationScaleRatio);
}];
[UIView addKeyframeWithRelativeStartTime:ratio relativeDuration:1 - ratio animations:^{
fullStar.transform = CGAffineTransformIdentity;
}];
} completion:^(BOOL finished) {
[self setImage:self.starImageFull forState:UIControlStateNormal];
[self setTintColor:self.fullTintColor];
[fullStar removeFromSuperview];
}];
}
Ah, I've managed to find a reason behind my bug and it works perfect now!
I used View Debugging to see if it's an UIImageView from animation is stuck on the button or the image of button itself is not set properly. I was surprised to see it's the button image that causing the issue. And then I looked through code again and realised I was changing the button image in the completion block after delay with no regard to if it's state has already changed. A simple fix to makeFullWithDelay: solved my issue.
} completion:^(BOOL finished) {
if (self.isFull) {
[self setImage:self.starImageFull forState:UIControlStateNormal];
[self setTintColor:self.fullTintColor];
}
[fullStar removeFromSuperview];
}];
I used whole evening trying to find the answer yesterday, and after I posted the question answer came by itself. Telling people about your problem really helps to go through your logic with more attention I guess. Thanks to everyone :-)
Should I delete the post or it might help someone in the future?
I am trying to animate a simple UIImageView, but it is not working. Here is my code:
In my header file, I have declared "light_bulb" as a UIImageView.
NSMutableArray *bulb_off_images = [[NSMutableArray alloc] init];
for (int loop = 0; loop < 11; loop++) {
if (loop >= 10) {
[bulb_off_images addObject:[UIImage imageNamed:[NSString stringWithFormat:#"bulb_off-%d.png", loop]]];
}
else {
[bulb_off_images addObject:[UIImage imageNamed:[NSString stringWithFormat:#"bulb_off-0%d.png", loop]]];
}
}
[UIView animateWithDuration:0.5 delay:2.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
light_bulb.animationImages = bulb_off_images;
} completion:^(BOOL finished){
if (finished) {
NSLog(#"Finished !!!!!");
}
}];
The code seems to run and the completion block is called but the animation does not happen.....
**Update 1 **
I tried doing the animation this way, but I need a way of stopping the animation from repeating itself. How do I know when to call "stopAnimating". Here is my code:
light_bulb.animationImages = bulb_off_images;
light_bulb.animationDuration = 0.5;
light_bulb.animationRepeatCount = 0;
[light_bulb startAnimating];
What am I doing wrong?
In regards to your original question, you're over-complicating your solution. UIImageView has animation methods built-in. It looks like you're trying to use them in conjunction with UIView's animation features, which won't work exactly the way you're planning. Review the docs for each, and decide which classes' animation features you would like to use.
(For images, UIImageView's animation methods seem to be the preferred way to go.)
As for your second question: in the documentation for UIImageView, the notes for animationRepeatCount state:
The default value is 0, which specifies to repeat the animation indefinitely.
Setting your animationRepeatCount to 1 should play the animation only once.
I am trying to get 'luke' to jump over an object to ensure that the game does not end, which he does but even if he avoids the object the game still ends, as if luke had not left his original location during the animation.
I used the UIView animateWithDuration in -(void)touchesBegin to try to achieve this.
[UIView animateWithDuration:3
animations:^
{
luke.center = CGPointMake(luke.center.x +0, luke.center.y -60);
}
completion:^(BOOL completed)
{
if (completed)
{
[UIView animateWithDuration:3
animations:^
{
luke.center = CGPointMake(luke.center.x +0, luke.center.y +60);
}];
}
I am also using CGRectIntersectsRect to tell me when the two objects collide
-(void)collision {
if (CGRectIntersectsRect(luke.frame, smallboulder.frame)) {
[self endgame];
}
if (CGRectIntersectsRect(luke.frame, largeboulder.frame)) {
[self endgame];
}
if (CGRectIntersectsRect(luke.frame, cactus.frame)) {
[self endgame];
}
finally can i use an animation set up as a NSArray to show movement when jumping.
Many thanks
JP
When using UIView animation methods to animate various properties, the animation is actually performed on something called the presentation layer. But when you use the view's frame accessor, it accesses the model layer (rather than the presentation layer), which holds the latest values (so it holds the designated frame after animation).
You should check for "luke"'s location using luke.layer.presentationLayer.frame.
I've got a view which is a 4x4 grid of images, and am trying to achieve the effect of every square, going from top to bottom and left to right, flipping over to reveal a new image. Should be a bit of a "ripple" effect, so the top left square starts flipping immediately, the [0,1] square flips after 0.05 seconds, [0,2] after 0.10 seconds, and so on, so that after (0.05*15) seconds the final square starts to flip.
My implementation is this: Each grid square is a UIView subclass, DMGridSquareView, which contains a UIImageView, and the following method:
- (void)flipToNewImage:(UIImage*)img {
AATileView *mySelf = self;
[UIView transitionWithView:self.imageView
duration:0.4
options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
self.imageView.image = img;
} completion:^(BOOL finished) {
NSLog(#"Finished flip");
}];
}
Then, in order to trigger the animation of all the grid squares, within the DMGridView, I call this method:
- (void)flipAllSquares:(NSArray*)newImages {
int numTiles = self.numColumns * self.numRows;
for (int idx=0; idx<numTiles; idx++) {
UIImage *newImg = [newImages objectAtIndex:idx];
[[self.gridSquareViews objectAtIndex:idx] performSelector:#selector(flipToNewImage:) withObject:newImg afterDelay:(0.05*idx)];
}
}
When running on the simulator, this works beautifully. But when running on my iPhone 5, it has an unexpected (to me, at least) behavior. The animations of the individual grid squares are nice and smooth, and at the correct speed, but the start times are off. They happen in staggered "chunks". So the first 2 squares might flip immediately, then the next 4 squares will simultaneously flip after a moment, then the next 3 flip after another moment, and so on. It's not consistent, but it's always in chunks like this.
Any idea how to fix this?
Thanks.
EDIT 1:
Sorry, it looks like I misunderstood your question. You do not want them to flip one after another, you would like each view to begin flipping very soon after the previous view has begun flipping.
Have you tried calling the method recursively from the completion block? This way you know the method is only being called after the previous image has completed the flip animation.
NSInteger imageFlipIndex = 0;
- (void)flipToNewImage:(UIImage*)img {
AATileView *mySelf = self;
[UIView transitionWithView:self.imageView
duration:0.4
options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
self.imageView.image = img;
} completion:^(BOOL finished) {
imageFlipIndex++;
if (imageFlipIndex < newImages.count) {
UIImage *newImg = [newImages objectAtIndex:idx];
[self flipToNewImage:newImg];
} else {
imageFlipIndex = 0;
}
NSLog(#"Finished flip");
}];
}
What happens if you try:
imageFlipIndex++;
if (imageFlipIndex < newImages.count) {
UIImage *newImg = [newImages objectAtIndex:idx];
[[self.gridSquareViews objectAtIndex:imageFlipIndex] performSelector:#selector(flipToNewImage:) withObject:newImg afterDelay:(0.05*idx)];
} else {
imageFlipIndex = 0;
}
inside the animations block?