1

I require to move 5 images across the screen and do transitional effects, such as rotate -> when rotate animation complete: move (when move complete: then repeat process)

I can get 1 image to do this, it works fine.

The problem I have is I need to do it for 5 images; the requirements are that all the images (or imageViews) need the animations to run at the same time.

I use a custom object which has a UIImageView inside it

-(void)stopRepeatingAnimationForView:(UIView *)view
{
    self.enableRepeatingAnimation = NO;
    [view.layer removeAllAnimations];
}

- (void)startRepeatingAnimationForView:(UIView *)view
{
    self.enableRepeatingAnimation = YES;
    [self repeatingAnimationForView:view];
}

- (void)repeatingAnimationForView:(UIView *)view
{
    //VICAirplane *planeObj = [self.airplanes objectAtIndex:0];
    for (VICAirplane *planeObj in self.airplanes) {


            [UIView animateWithDuration:3
                                  delay:1
                                options:UIViewAnimationOptionCurveLinear
                             animations:^{
                                 planeObj.imageView.transform = CGAffineTransformRotate(planeObj.imageView.transform, planeObj.angleToRotate);
                             } completion:^(BOOL finished) {

                                 if (finished==YES) {
                                     [UIView animateWithDuration:5
                                                           delay:0.0
                                                         options:UIViewAnimationOptionCurveLinear
                                                      animations:^{

                                                          planeObj.imageView.center = planeObj.rotateToLocation;

                                                      } completion:^(BOOL finished) {
                                                          if (finished==YES) {
                                                              if ( self.enableRepeatingAnimation )
                                                              {
                                                                  CGPoint rotateToLocation = [self generateRandomLocation];
                                                                  planeObj.rotateToLocation = rotateToLocation;

                                                                  [self performSelector:@selector(repeatingAnimationForView:) withObject:view afterDelay:0];
                                                              }
                                                          }
                                                      }];
                                 }
                             }];

    } // next


}

If remove the loop through the array or simply make it 1 length array the animation works fine.

But if I add multiple the animations are all the same time, and they start losing the finished state and end up doing a lot of mess.

What I'd like to do is:

  1. Loop through my 5 images
  2. Perform my rotate -> move -> repeat (forever) animation on each image

But I'm not sure how to make it work for multiple uiimageviews stored in an array

Is there a way to do this?

Atilla Jax
  • 615
  • 5
  • 15

2 Answers2

0

If I understand your question correctly, the following code should do what you want. It is designed to do the following things, in this order:

  1. Rotate all items in the view/airplane/whatever array to their new rotations at the same time and over the same duration.
  2. Translate all items in the view/airplane/whatever array to their new locations at the same time and over the same duration.
  3. At the end of #2, repeat, beginning with #1.

Now, for my code, I created VICAirplane as a simple subclass of UIView that has a single property: @property (assign,nonatomic) CGPoint rotateToLocation.

Basically, you were trying to have a single for loop to iterate over your objects, but it will work better if you iterate your array during each animation. You can definitely chain them in the way I'm showing below, but you could also create separate methods for each "link" of the animation chain and then iterate over the array once per method. In either case, you need to iterate the array in/during the animation block, not outside of it. When you iterate outside of it (as your code does above), you are creating a separate animation block for each object, all of which will be handled independently.

Anyway, the code below creates 5 colored squares that are rotated and then translated in random ways in a continuous animation loop.

//  VICAirplane.h
#import <UIKit/UIKit.h>
@interface VICAirplane : UIView
@property (assign, nonatomic) CGPoint rotateToLocation;
@end
//  VICAirplane.m
#import "VICAirplane.h"
@implementation VICAirplane
@end

//  ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
//  ViewController.m
#import "ViewController.h"
#import "VICAirplane.h"
@interface ViewController () {

}
@property (strong, nonatomic) NSMutableArray *views;
@property (assign, nonatomic) BOOL enableRepeatingAnimation;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUserInterface];
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self startRepeatingAnimation];
}
- (void)setupUserInterface {
    self.views = [NSMutableArray array];
    for (int i = 0; i < 5; i = i + 1) {
        VICAirplane *newView = [[VICAirplane alloc] initWithFrame:CGRectMake((i * 100) + 20, (i * 100) + 20, 80, 80)];
        switch (i % 4) {
            case 0:
                newView.backgroundColor = [UIColor blueColor];
                break;
            case 1:
                newView.backgroundColor = [UIColor redColor];
                break;
            case 2:
                newView.backgroundColor = [UIColor greenColor];
                break;
            case 3:
                newView.backgroundColor = [UIColor brownColor];
                break;
            case 4:
                newView.backgroundColor = [UIColor purpleColor];
                break;
            default:
                break;
        }
        [self.views addObject:newView];
        [self.view addSubview:newView];
    }
}
- (CGPoint)generateRandomLocation {
    CGFloat x = (CGFloat)arc4random_uniform(600);
    CGFloat y = (CGFloat)arc4random_uniform(600);
    return CGPointMake(x, y);
}
-(void)stopRepeatingAnimation {
    self.enableRepeatingAnimation = NO;
    [self.view.layer removeAllAnimations];
}
- (void)startRepeatingAnimation {
    self.enableRepeatingAnimation = YES;
    for (VICAirplane *subview in self.views) {
        subview.rotateToLocation = [self generateRandomLocation];
    }
    [self repeatingAnimation];
}
- (void)repeatingAnimation {
    [UIView animateWithDuration:3
                          delay:1
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         for (UIView *subview in self.views) {
                             // Just a random rotation between -2π and 2π
                             CGFloat rotationAngle = (arc4random_uniform(400 * M_PI) - (200 * M_PI)) / 100;
                             subview.transform = CGAffineTransformRotate(subview.transform, rotationAngle);
                         }
                     }
                     completion:^(BOOL finished) {
                         if (finished==YES) {
                             [UIView animateWithDuration:5
                                                   delay:0.0
                                                 options:UIViewAnimationOptionCurveLinear
                                              animations:^{
                                                  for (VICAirplane *subview in self.views) {
                                                      subview.center = subview.rotateToLocation;
                                                  }
                                              }
                                              completion:^(BOOL finished) {
                                                  if (finished==YES) {
                                                      if (self.enableRepeatingAnimation) {
                                                          for (VICAirplane *subview in self.views) {
                                                              subview.rotateToLocation = [self generateRandomLocation];
                                                          }
                                                          [self performSelector:@selector(repeatingAnimation) withObject:nil afterDelay:0.0f];
                                                      }
                                                  }
                                              }
                              ];
                         }
                     }
     ];
}
@end
mbm29414
  • 11,558
  • 6
  • 56
  • 87
0

I just put my loop in a different part of my app and it works much better now.

Originally I was doing this:

- (void)repeatingAnimationForView:(UIView *)view
{
    for (VICAirplane *planeObj in self.airplanes) {
    }
}

I didn't need to put the for-loop in my startRepeatingAnimationForView method and just passed params to the same method

Instead I should have put it here (which I have now done and works much better than my original)

-(void)stopRepeatingAnimationForView:(UIView *)view
{
    self.enableRepeatingAnimation = NO;
    [view.layer removeAllAnimations];
}

- (void)startRepeatingAnimationForView:(UIView *)view
{
    self.enableRepeatingAnimation = YES;
    for (VICAirplane *planeObj in self.airplanes) {
        [self repeatAnimationWithParams:@{@"view":view, @"planeObj": planeObj}];
    }
}

-(void) repeatAnimationWithParams:(NSDictionary *)params
{
    VICAirplane *planeObj = params[@"planeObj"];

    [UIView animateWithDuration:2
                          delay:1
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         planeObj.imageView.transform = CGAffineTransformRotate(planeObj.imageView.transform, planeObj.angleToRotate);
                     } completion:^(BOOL finished) {

                         if (finished==YES) {
                             [UIView animateWithDuration:5
                                                   delay:0.0
                                                 options:UIViewAnimationOptionCurveLinear
                                              animations:^{

                                                  planeObj.imageView.center = planeObj.rotateToLocation;

                                              } completion:^(BOOL finished) {
                                                  if (finished==YES) {
                                                      if ( self.enableRepeatingAnimation )
                                                      {
                                                          CGPoint rotateToLocation = [self generateRandomLocation];
                                                          planeObj.rotateToLocation = rotateToLocation;

                                                          [self performSelector:@selector(repeatAnimationWithParams:)
                                                                     withObject:params
                                                                     afterDelay:0];
                                                      }
                                                  }
                                              }];
                         }
                     }];

}
Atilla Jax
  • 615
  • 5
  • 15
  • You're still spawning multiple, different, unrelated animation blocks (1 per object/view). If something goes wrong (unlikely, but possible), your animations could get out of sync. I'd recommend queueing them all together like I did in my code sample. – mbm29414 Jan 19 '15 at 17:51