9

I have a view with a scroll view in my app (a percentage calculator). I am trying to animate the view to fly out and then fly in again from the left albeit with different contents, on the press of a button. Here's my code:

func next()
{
    UIView.animateWithDuration(0.5, animations: {
        self.mainView.frame = CGRectMake(-500, self.view.bounds.height/2-self.mainHeight/2, self.mainWidth, self.mainHeight)
        self.mainView.center.x -= 600
    }) { (success) in
        self.mainView.center.x += 1200    
    }

    UIView.animateWithDuration(0.5, animations: {
        self.mainView.frame = CGRectMake(self.view.bounds.width/2-self.mainWidth/2, self.view.bounds.height/2-self.mainHeight/2, self.mainWidth, self.mainHeight)
    }) { (success) in
        print("Animation Successful!")
        self.drawView()
    }
}

I don't understand why, but the animation doesn't occur, the view disappears completely and I see "Animation Successful" in the logs. To be clear, I have created the view programmatically.

Here's the code in viewDidLoad(); the view displays correctly when it's called:

    mainHeight = self.view.bounds.height * 0.85
    mainWidth = self.view.bounds.width * 0.85
    let viewHeight = self.view.bounds.height
    let viewWidth = self.view.bounds.width

    mainView.frame = CGRectMake(viewWidth/2-mainWidth/2, viewHeight/2-mainHeight/2, mainWidth, mainHeight)
    mainView.layer.cornerRadius = 8
    mainView.layer.masksToBounds = true
    mainView.backgroundColor = getColor(0, green: 179, blue: 164)
    self.view.addSubview(mainView)

    scrollView.frame = CGRectMake(0, 0, mainWidth, mainHeight)
    scrollView.contentSize = CGSizeMake(mainWidth, 800)
    self.mainView.addSubview(scrollView)

    contentView.frame = CGRectMake(0, 0, mainWidth, 800);
    contentView.backgroundColor = getColor(0, green: 179, blue: 164)
    contentView.layer.masksToBounds = true
    contentView.clipsToBounds = true
    self.scrollView.addSubview(contentView)
Swapnil Dhanwal
  • 399
  • 2
  • 6
  • 17

2 Answers2

17

Place self.layoutViewIfNeeded() before frame/center changes. Additionally you may need to put your next animation within a completion handler of the next animation.

func next() {
    UIView.animateWithDuration(0.5, animations: {
        self.view.layoutIfNeeded() // add this
        self.mainView.frame = CGRectMake(-500, self.view.bounds.height/2-self.mainHeight/2, self.mainWidth, self.mainHeight)
        self.mainView.center.x -= 600

    }) { (success) in
        self.view.layoutIfNeeded() // add this
        self.mainView.center.x += 1200

        // your next animation within a completion handler of previous animation
        UIView.animateWithDuration(0.5, animations: {

            self.mainView.frame = CGRectMake(self.view.bounds.width/2-self.mainWidth/2, self.view.bounds.height/2-self.mainHeight/2, self.mainWidth, self.mainHeight)

        }) { (success) in

            print("Animation Successful!")
            self.drawView()
        }
    }
}   

I haven't tested it yet, perhaps some changes or lines ordering is needed.

pedrouan
  • 12,762
  • 3
  • 58
  • 74
  • Thank you! It works perfectly! I guess my error was to not write the successive animation code in the completion handler. Additionally, do you know of any method by which I can control the velocity of the animation i.e. its starts slow, then speeds up and finally slows down again? – Swapnil Dhanwal Sep 25 '16 at 10:16
  • Sure, you are free to use more extended method for this purpose, like `UIView.animate(withDuration:delay:options:animations:)` and set options value to `UIViewAnimationOptions.curveEaseInOut` so the movement will speed up in the start and slow down in the end of the animation. – pedrouan Sep 25 '16 at 10:27
  • There was a problem with my code when I at interview with a huge game company. I wish I had see this answer before it was. Thanks by the way – eemrah Jun 24 '19 at 11:47
  • 1
    For me worked placing layoutIfNeeded() method at the _end_ of the animation closure, not in the beginning. – rommex Aug 28 '19 at 10:06
14

In Swift 3, I had to set the frame and then call layoutIfNeeded().

For example, an animation with spring:

UIView.animate(withDuration: 0.3, delay: 0.1, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
        self.subView.frame = CGRect(x: 0, y: 0, width: 500, height: 200)
        self.view.layoutIfNeeded()
    }) { (_) in
        // Follow up animations...
    }
OffensivelyBad
  • 621
  • 8
  • 16