-1

I am trying to animate an UIImageView to move across the screen. In order to make the motion smooth, I need to use about 60 increments. The code works well for increments 0-10 with each new increment happening after one second pause. However, for increments 10-20 it pauses 2 seconds and then executes the loop twice. For 21-30 it pauses 3 seconds and executes the loop three times, so on. Below is my loop:

    for i in 0...30 {
        let when = DispatchTime.now() + .seconds(i)
        DispatchQueue.main.asyncAfter(deadline: when) {
            let date = Date()
            let calendar = Calendar.current
            let second = calendar.component(.second, from: date)
            print("delay count = \(i), time = \(second))")
            self.ballView.frame = CGRect(x: ballPositionX, y: self.view.frame.size.height * 0.45, width: self.view.frame.size.width * 0.2, height: self.view.frame.size.height * 0.2)
            ballPositionX = ballPositionX + ballMoveX
        }
    }

You can see in the console log below the additive delay in loop execution by comparing the index (delay count) with the seconds (time) when the loop happens.

   delay count = 0, time = 10)
   delay count = 1, time = 11)
   delay count = 2, time = 12)
   delay count = 3, time = 13)
   delay count = 4, time = 14)
   delay count = 5, time = 16)
   delay count = 6, time = 17)
   delay count = 7, time = 18)
   delay count = 8, time = 19)
   delay count = 9, time = 19)
   delay count = 10, time = 21)
   delay count = 11, time = 22)
   delay count = 12, time = 22)
   delay count = 13, time = 25)
   delay count = 14, time = 25)
   delay count = 15, time = 27)
   delay count = 16, time = 27)
   delay count = 17, time = 29)
   delay count = 18, time = 29)
   delay count = 19, time = 31)
   delay count = 20, time = 31)
   delay count = 21, time = 33)
   delay count = 22, time = 33)
   delay count = 23, time = 33)
   delay count = 24, time = 37)
   delay count = 25, time = 37)
   delay count = 26, time = 37)
   delay count = 27, time = 40)
   delay count = 28, time = 40)
   delay count = 29, time = 40)
   delay count = 30, time = 43)

I have looked at other implementations for a delay in a loop, but generally their loops never go past 10 cycles.

Delph2
  • 119
  • 1
  • 9
  • 1
    why don't you just UIView.Animate with a time duration on a constraint constant, then call view.layoutIfNeeded() rather than what you're doing currently? – Jay Mar 21 '17 at 23:32
  • Agree with Jay, you could also look up 3rd party animation libraries that make it even easier. – Daniel Legler Mar 21 '17 at 23:34

1 Answers1

0

The best way to achieve an animation is by setting parameter to view that you want to animate then call UIView.animate with one of its overloads and call for update the specific part

In your case you are updating the layout so you should call layoutIfNeeded

here is an example:

//calculating final frame (origin and size)
let x = ballPossitionX * 30
let y = self.view.frame.size.height * 0.45
let width = self.view.frame.size.width * 0.2
let height = self.view.frame.size.height * 0.2

ballView.frame = CGRect(x: x, y: y, width: width, height: height

let animationTime = 2.0

UIView.animate(withDuration: animationTime) {
   self.view.layoutIfNeeded()
}

There are also some animated properties like alpha which can be added directly in animate block

zombie
  • 5,069
  • 3
  • 25
  • 54
  • The UIView.animate worked great. I am still new to Xcode so I did not know about this feature. As for the loop delay not working properly past 10 passes, I guess it is a bug. Thanks for the help. – Delph2 Mar 22 '17 at 20:30
  • @Delph2 I'm not sure if this solved your problem or you still have some – zombie Mar 22 '17 at 20:43
  • You solved my problem in that my app is working the way I want it. The delay in the loop not working properly is now just a curiosity, but not important for me to understand. Thanks very much for your help. – Delph2 Mar 23 '17 at 03:18