skip to Main Content

I want to create a looping animation like this GIF:

enter image description here

I have one picture, these is clouds. I want to see an animation of looping clouds when I launch the app. I’m adding 2 imageView with the same cloud picture. I use this code to do this:

cloudsImageView1.frame.origin.x = 0
cloudsImageView2.frame.origin.x = screenSize
        
UIView.animate(withDuration: 20.0, delay: 0.0, options: [.repeat, .curveLinear], animations: {
    self.cloudsImageView1.frame = self.cloudsImageView1.frame.offsetBy(dx: -1 * screenSize, dy: 0.0)
            
    self.cloudsImageView2.frame = self.cloudsImageView2.frame.offsetBy(dx: -1 * screenSize, dy: 0.0)
}, completion: nil)

But I have bad results with wrong direction and disappearing images. How to fix it?

2

Answers


  1. To fix the issue with disappearing images and wrong direction, ensure that you reset the position of the image views once they move out of the screen. You can achieve a seamless loop by repositioning the views in the completion block of the animation.

    Here’s a corrected example:

    let screenSize = UIScreen.main.bounds.width
    
    cloudsImageView1.frame.origin.x = 0
    cloudsImageView2.frame.origin.x = screenSize
    
    func animateClouds() {
        UIView.animate(withDuration: 20.0, delay: 0.0, options: [.curveLinear], animations: {
            self.cloudsImageView1.frame.origin.x -= screenSize
            self.cloudsImageView2.frame.origin.x -= screenSize
        }) { _ in
            // Reset positions for seamless loop
            if self.cloudsImageView1.frame.maxX <= 0 {
                self.cloudsImageView1.frame.origin.x = screenSize
            }
            if self.cloudsImageView2.frame.maxX <= 0 {
                self.cloudsImageView2.frame.origin.x = screenSize
            }
            self.animateClouds()
        }
    }
    
    // Start the animation
    animateClouds()
    

    This ensures the clouds loop seamlessly without disappearing or moving in the wrong direction.

    Login or Signup to reply.
  2. We can do this by adding the image views as subviews of a UIView, and then animate that "container" view.

    Since we are using a single image, we can run a repeating animation. When the animation "re-starts," it will reset the container view’s .origin.x to 0.0 and it will appear as a seamless, infinite scroll.

    Quick example code:

    class ScrollingCloudsViewController: UIViewController {
        
        let imageContainerView = UIView()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            view.backgroundColor = .systemBackground
        }
        
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            
            // this can be called multiple times
            //  so we only want to setup and add the views once
            
            if imageContainerView.superview == nil {
                
                guard let img = UIImage(named: "singleCloud") else { return }
                
                // full width of view
                let w = view.frame.width
                
                // whatever height you want to use
                let h = 240.0
                
                // create two image views with the same image
                let v1 = UIImageView(image: img)
                let v2 = UIImageView(image: img)
                
                // set them up
                v1.frame = .init(x: 0.0, y: 0.0, width: w, height: h)
                v2.frame = .init(x: w, y: 0.0, width: w, height: h)
                
                imageContainerView.addSubview(v1)
                imageContainerView.addSubview(v2)
                
                imageContainerView.frame = .init(x: 0.0, y: 0.0, width: w * 2.0, height: h)
                view.addSubview(imageContainerView)
                
            }
            
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            
            // full width of view
            let w = view.frame.width
            
            UIView.animate(withDuration: 20.0, delay: 0.0, options: [.repeat, .curveLinear], animations: { [weak self] in
                guard let self else { return }
                self.imageContainerView.frame.origin.x = -w
            }, completion: { _ in
                // if we want to do anything on completion
            })
        }
        
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search