Search code examples
swiftanimationnslayoutconstraint

Animation is not showing as expected in swift


I have taken image and I want flyView to be drop from top middle to 200 from top and vanishes. for that tried like this

code: here I have given constraints for flyView as flyView.centerXAnchor and flyView.topAnchor then why flyView coming inside from left of the screen?? and when animation happening then why whole screen moves badly. i have clear tableview and below submit button as well but whole screen moving with animation why? how to fix this? please guide me.

class TableAnimationViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!

let flyView = UIImageView()
override func viewDidLoad() {
    super.viewDidLoad()
    tableView.delegate = self
    tableView.dataSource = self
    
    self.flyView.image = UIImage(named: "offer_img")
    self.view.addSubview(flyView)
    
    self.flyView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        flyView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50),
        flyView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
        flyView.widthAnchor.constraint(equalToConstant: 100),
        flyView.heightAnchor.constraint(equalToConstant: 100)
    ])
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    UIView.animate(withDuration: 5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: [.curveEaseOut, .repeat], animations: { [self] in
        flyView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 200).isActive = true
        flyView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
        flyView.alpha = 0
        self.view.layoutIfNeeded()
    })
}
}

o/p

enter image description here


Solution

  • Your code has a number of issues.

    • You are trying to start your animation in viewWillAppear. At that point the your view controller's auto layout hasn't set up the positions of your views yet.

    • You are installing new AutoLayout constraints on your flyView instead of changing the settings of the existing constraints. Don't do that. Save the constraints you want to animate as properties and then animate changes to them.

    • You are re-installing a center X constraint that doesn't actually change anything. Don't do that either.

    I created a project based on your code that works. Here is what the view controller code looks like:

    class ViewController: UIViewController {
        
        @IBOutlet weak var animateButton: UIButton!
        
        var topAnchor: NSLayoutConstraint? = nil
        let flyView = UIImageView()
        override func viewDidLoad() {
            super.viewDidLoad()
            
            self.flyView.image = UIImage(named: "Scampers")
            self.flyView.translatesAutoresizingMaskIntoConstraints = false
            self.view.addSubview(flyView)
            
            topAnchor = flyView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50)
            NSLayoutConstraint.activate([
                topAnchor!,
                flyView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
                flyView.widthAnchor.constraint(equalToConstant: 100),
                flyView.heightAnchor.constraint(equalToConstant: 100)
            ])
        }
        
        @IBAction func handleAnimateButton(_ sender: UIButton?) {
            animateButton?.isEnabled = false
            UIView.animate(withDuration: 2, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: [.curveEaseOut, .repeat], animations: { [self] in
                topAnchor?.constant = 200
                flyView.alpha = 0
                self.view.layoutIfNeeded()
            })
        }
        override func viewDidAppear(_ animated: Bool) {
            //handleAnimateButton(nil)
        }
    }
    

    (I pulled out the animation into a button action, and also showed that you could call the animation code from viewDidAppear(_:) if desired.)

    I don't know of a way to include spring damping in UIView keyframe animations. I'd probably drop down to the lower-level CAAnimation and create an animation group that included a spring animation and a fade animation. That would work, but Core Animation is a lot fussier and harder to figure out than UIView animation.

    Note that there's not much point to having a spring animation and setting the Alpha to 0 at the same time. The image view fades away before the sprint animation is visible.

    You might want to instead create a keyframe animation that first does the animate down and then the fade animation.

    I had to use my own image for the FlyView, but below is what the animation looks like.

    Edit:

    If you refactor your animation code to use keyframe animations that might look something like this:

    @IBAction func handleAnimateButton(_ sender: UIButton?) {
        animateButton?.isEnabled = false
        UIView.animateKeyframes(withDuration: 1, delay: 0,
                                options: [UIView.KeyframeAnimationOptions(rawValue:  UIView.AnimationOptions.curveEaseOut.rawValue), .repeat]) {
            UIView.addKeyframe(withRelativeStartTime:0, relativeDuration: 0.75 ) {[self] in
                topAnchor?.constant = 200
                self.view.layoutIfNeeded()
            }
            UIView.addKeyframe(withRelativeStartTime:0.75, relativeDuration: 0.25 ) {[self] in
                flyView.alpha = 0
            }
        }
    }
    

    (Keyframe animations actually do support animation curves like ease in, ease out, and ease in/out, but the constants aren't defined, so you have to play games with raw values in order to use them.)

    enter image description here