Swift iOS multiple successive multiple animations with same UIView?

11,570

Solution 1

i think the error is that you mix here 2 things:

  • UIView.animate -> animate controls and properties
  • UIImageView.startAnimating -> start a loop of images in an UIImageView

but they don't do the same they are very independent. but UIView animation is normally for an other use case. only one thing is that they maybe have the same duration like your UIImageView animation, but you don't set the duration of your UIImageView. maybe when you set the animation duration of your image view to the duration of UIView animation then it is done on the same time range.

myImageView.animationDuration = 2;

and for the second loop

myImageView.animationDuration = 4;

Other Solutions

the thing is you need to know when an image loop ist completed. but there is no event for this (i did not found any)

there are some solutions on StackOverflow for this:

1 performSelector afterDelay

Set a timer to fire after the duration is done. for this you need also to add an animation duration to your image view:

myImageView.animationDuration = 0.7;

solution from here: how to do imageView.startAnimating() with completion in swift?:

When the button is pressed you can call a function with a delay

self.performSelector("afterAnimation", withObject: nil, afterDelay: imageView1.animationDuration)

Then stop the animation and add the last image of imageArray to the imageView in the afterAnimation function

func afterAnimation() {
    imageView1.stopAnimating()
    imageView1.image = imageArray.last
}

2 Timer

this is similar to performSelector afterDelay but with NSTimer.

you find a description here UIImageView startAnimating: How do you get it to stop on the last image in the series? :

1) Set the animation images property and start the animation (as in your code in the question)

2) Schedule an NSTimer to go off in 'animationDuration' seconds time

3) When the timer goes off, [...]

add to point 3: then start the next animation

3 CATransaction

solution from here: Cocoa animationImages finish detection (you need to convert it to swift 3 syntax but it can be a starting point

Very old question, but I needed a fix now, this works for me:

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    DLog(@"Animation did finish.");
}];

UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.window.bounds];
imageView.animationDuration = 0.3 * 4.0;
imageView.animationImages = @[[UIImage AHZImageNamed:@"Default2"],
                              [UIImage AHZImageNamed:@"Default3"],
                              [UIImage AHZImageNamed:@"Default4"],
                              [UIImage AHZImageNamed:@"Default5"]];
imageView.animationRepeatCount = 1;
[self.window addSubview:imageView];

[imageView startAnimating];

[CATransaction commit];

4 Offtopic: Manual do the Image Animation

thats a little offtopic, because here they do the image animation manually. for your use case you just change the logic which image from index is visible. count forward until last image, then backwards until first image. and then stop the loop. not nice solution but in this solution is added a image transition animation:

Solution from here: Adding Image Transition Animation in Swift

class ViewController: UIViewController {
        @IBOutlet weak var imageView: UIImageView!

        let images = [
                UIImage(named: "brooklyn-bridge.jpg")!,
                UIImage(named: "grand-central-terminal.jpg")!,
                UIImage(named: "new-york-city.jpg"),
                UIImage(named: "one-world-trade-center.jpg")!,
                UIImage(named: "rain.jpg")!,
                UIImage(named: "wall-street.jpg")!]
        var index = 0
        let animationDuration: NSTimeInterval = 0.25
        let switchingInterval: NSTimeInterval = 3

        override func viewDidLoad() {
                super.viewDidLoad()

                imageView.image = images[index++]
                animateImageView()
        }

        func animateImageView() {
                CATransaction.begin()

                CATransaction.setAnimationDuration(animationDuration)
                CATransaction.setCompletionBlock {
                        let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(self.switchingInterval * NSTimeInterval(NSEC_PER_SEC)))
                        dispatch_after(delay, dispatch_get_main_queue()) {
                                self.animateImageView()
                        }
                }

                let transition = CATransition()
                transition.type = kCATransitionFade
                /*
                transition.type = kCATransitionPush
                transition.subtype = kCATransitionFromRight
                */
                imageView.layer.addAnimation(transition, forKey: kCATransition)
                imageView.image = images[index]

                CATransaction.commit()

                index = index < images.count - 1 ? index + 1 : 0
        }
}

Implement it as a custom image view would be better.

Solution 2

I know you said that this didn't work for you, but if you just want to execute animations right after another, I believe this really is the best option.

You should be able to do something like this:

let imageView = UIImageView()
UIView.animate(withDuration: 0.5, animations: {
    //animation 1
}, completion: { (value: Bool) in
    UIView.animate(withDuration: 1.0, animations: {
        //animation 2
    })
})

This does one animation directly after another, which a longer duration. Of course you still need to define what the animations are, but I hope this helps.

Solution 3

I'm not sure if this is the best way to do this, but I've found a solution to my problem. Please give feedback if there is a better way. I was able to accomplish multiple animations in a single UIImageView consecutively by using: self.perform(#selector(ViewController.afterAnimation), with: nil, afterDelay: 4.0)

This calls the afterAnimation function:

 func afterAnimation() {
         imageView.stopAnimating()
         imageView.animationImages = images2
         imageView.animationDuration = 6.0
         imageView.animationRepeatCount = 1
         imageView.startAnimating()
     }

I needed animations to each last a specific amount of time and to be able to chain many of them together. It solves my problem.

Share:
11,570
schulzey
Author by

schulzey

Updated on June 04, 2022

Comments

  • schulzey
    schulzey almost 2 years

    So here is where I am at now. I still haven't been able to get it to work and I've tried a lot of variations. Here is my original code for a single animation.

    import UIKit

    class ViewController: UIViewController {
    
        @IBOutlet weak var imageView: UIImageView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            var images = [UIImage]()
            var images2 = [UIImage]()
    
            images += [#imageLiteral(resourceName: "circle1"), #imageLiteral(resourceName: "circle2"), #imageLiteral(resourceName: "circle3"), #imageLiteral(resourceName: "circle4")]
            images2 += [#imageLiteral(resourceName: "circle4"), #imageLiteral(resourceName: "circle3"), #imageLiteral(resourceName: "circle2"), #imageLiteral(resourceName: "circle1")]
    
            imageView.animationImages = images
            imageView.animationDuration = 4.0
            imageView.animationRepeatCount = 2
            imageView.startAnimating()
        }
    }
    

    I am able to animate the first images array with the properties I've set for duration and count. However, I just do not understand how I would write this to add a second set of images to animate immediately after. I know I need to use this somehow:

    UIView
        .animate(withDuration: 4.0,
                 animations: {
                    //animation 1
        },
                 completion: { _ in
                    UIView.animate(withDuration: 6.0,
                                   animations: {
                                    //animation 2
                    })
        })
    

    Can someone show me how I would write this given my current images arrays using the same imageView object to display both animations? I want my first animation (images) to have a duration of 4 seconds and my second animation (images2) to have a duration of 6 second immediately following the first animation. I can't figure out what I need inside the animations parameter.