Frames changing suddenly instead of animating [iOS]
Asked Answered
R

2

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)
Robbery answered 25/9, 2016 at 8:38 Comment(2)
what is the initial frame of the "mainView"?Midsummer
Apologies for the confusion. I am editing the answer with the initialisations!Robbery
M
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.

Millen answered 25/9, 2016 at 8:51 Comment(4)
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?Robbery
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.Millen
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 wayUnidirectional
For me worked placing layoutIfNeeded() method at the end of the animation closure, not in the beginning.Coca
M
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...
    }
Matheson answered 7/2, 2018 at 16:31 Comment(1)
Exactly! I found this as well before glancing at your answerCoca

© 2022 - 2024 — McMap. All rights reserved.