How to animate a UIView with constraints in Swift?

2019-01-22 01:46发布

In my contrived example, I have the following single view:

enter image description here

As you can see, it consists of a few simple constraints:

  1. Align horizontal and vertical centers,
  2. Height (set to a constant)
  3. Leading and trailing spaces (set to constant)

What I'm seeking to achieve is to have this redish/pinkish view "come in" from the top. Traditionally, in a constraint-less world, I would simply modify the frame inside of UIView.animateWithDuration, however I'm not sure how I do the similar in a constraint world.

To reiterate my question, how can I make my view start out of scene and animate the view flying in from the top?

I've considered animating the vertical centers constraint (and subsequently calling layoutIfNeeded), but it's not achieving the desired effect.

Thanks for your help.

5条回答
Explosion°爆炸
2楼-- · 2019-01-22 01:56

For me the best way for animate Swift 3 & 4

self.constraint.constant = -150

UIView.animate(withDuration: 0.45) { [weak self] in
    self?.view.layoutIfNeeded()
}
查看更多
Rolldiameter
3楼-- · 2019-01-22 02:01

What you want is in fact rather simple, and I will detail how it should be done without messing with priorities or funky constants. This way we can get an animation that will work on any screen size.

The TL;DR is that you need to configure your constraints and then call layoutIfNeeded inside your animation block to animate the changes. For more see this SO post.

So we need two sets of constraints for hiding and showing the view. In viewDidLoad the view will be hidden, and then in viewDidAppear or wherever we want that view to slide in.

Storyboard/XIB Setup

So the first thing is to configure the constraints that won't be changing in IB (or code, whichever you are comfortable with). Namely; the height of the red view, and its leading and trailing space to the container. So your constraints might look like this (note: I haven't set a width constraint but let the leading and trailing spaces define the width):

Constraints

Now you will see that IB will warn you that there is no constraint configured for the Y position of your view (hence the red dotted lines)

View with warnings

You can now add the top space to container constraint and set it as a placeholder constraint (checkbox that says "remove at build time"). We do this because we want to control where the view is programatically.

enter image description here

This means that this constraint will not exist once the view is loaded but serves to remove all your warnings as you are telling IB that you know how to deal with this constraint when the view is loaded.

Coding Time

Now we need a method to hide the view, a method to show the view. For this we are going to store the Y positioning constraint on the view and then animate it. Our viewController could look like this:

@IBOutlet weak var redView: UIView!
var redViewYPositionConstraint: NSLayoutConstraint?

override func viewDidLoad() {
    super.viewDidLoad()
    self.hideRedViewAnimated(false)
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.showRedViewAnimated(true)
}

Hiding

Our method to hide the view can simply remove the position constraint and then add one with the red views bottom equal to the view controller's view top:

func hideRedViewAnimated(animated: Bool) {
    //remove current constraint
    self.removeRedViewYPositionConstraint()
    let hideConstraint = NSLayoutConstraint(item: self.redView,
        attribute: .Bottom,
        relatedBy: .Equal,
        toItem: self.view,
        attribute: .Top,
        multiplier: 1,
        constant: 0)
    self.redViewYPositionConstraint = hideConstraint
    self.view.addConstraint(hideConstraint)
    //animate changes
    self.performConstraintLayout(animated: animated)
}

Showing

Similarly our show constraint will move the view's center Y to the controllers center Y:

func showRedViewAnimated(animated: Bool) {
    //remove current constraint
    self.removeRedViewYPositionConstraint()
    let centerYConstraint = NSLayoutConstraint(item: self.redView,
        attribute: .CenterY,
        relatedBy: .Equal,
        toItem: self.view,
        attribute: .CenterY,
        multiplier: 1,
        constant: 0)
    self.redViewYPositionConstraint = centerYConstraint
    self.view.addConstraint(centerYConstraint)
    //animate changes
    self.performConstraintLayout(animated: animated)
}

Convenience Methods

For completeness the convenience methods I have used look like this:

func performConstraintLayout(animated animated: Bool) {
    if animated == true {
          UIView.animateWithDuration(1,
          delay: 0,
          usingSpringWithDamping: 0.5,
          initialSpringVelocity: 0.6,
          options: .BeginFromCurrentState,
          animations: { () -> Void in
             self.view.layoutIfNeeded()
          }, completion: nil)
     } else {
          self.view.layoutIfNeeded()
     }
}

func removeRedViewYPositionConstraint() {
    if redViewYPositionConstraint != nil {
        self.view.removeConstraint(self.redViewYPositionConstraint!)
        self.redViewYPositionConstraint = nil
    }
}

You can also check if the red view is visible by using some CGRect math:

func isRedViewVisible() -> Bool {
    return CGRectContainsPoint(self.view.bounds, self.redView.frame.origin)
}
查看更多
我命由我不由天
4楼-- · 2019-01-22 02:04

Try this:

class ViewController: UIViewController {

    // red view
    @IBOutlet weak var myView: UIView!
    //  vertical align contraint
    @IBOutlet weak var verticalConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        verticalConstraint.constant = (myView.bounds.height + self.view.bounds.height)/2
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.view.layoutIfNeeded()
        UIView.animateWithDuration(0.5) {
            self.verticalConstraint.constant = 0
            self.view.layoutIfNeeded()
        }
    }

}

Be careful about firstItem and secondItem order in the constraint. The above code assumes Superview is the firstItem:

screenshot


Alternative way is to define two constraints:

  • Center Y Alignment constraint (Superview.CenterY == View.CenterY) priority = 750
  • Vertical Space Constraint (SuperView.Top == View.Bottom) priority = 500

screenshot

And adjust the priority to determine which contraint should be adopted.

class ViewController: UIViewController {

    //  vertical align contraint
    @IBOutlet weak var centerYConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        centerYConstraint.priority = 250
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.view.layoutIfNeeded()
        UIView.animateWithDuration(0.5) {
            self.centerYConstraint.priority = 750
            self.view.layoutIfNeeded()
        }
    }

}
查看更多
The star\"
5楼-- · 2019-01-22 02:07

What you can do is to add the following code in your viewDidAppear method. You firstly make a IBOutlet property of your view's center Y constraint, and then change its constant value.

self.centerYConstraint.constant = 500.0
self.view.layoutIfNeeded()

UIView.animateWithDuration(Double(0.5), animations: {
        self.centerYConstraint.constant = 0
        self.view.layoutIfNeeded()
    })
查看更多
【Aperson】
6楼-- · 2019-01-22 02:11

For anyone looking for simple animation like the request:

For the slide up animation you need to use [...] the CGAffineTransformMakeTranslation(x, y).

The reason is that for a slide up animation you need first move the view off screen and then bring it back to its original position. So in your viewDidLoad just use:

yourView.transform = CGAffineTransformMakeTranslation(0, 500)

This moves the view off screen, in this case at the bottom. Now in the viewDidAppear you can show your view with:

UIView.animateWithDuration(0.7, delay: 0.0, usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5, options: [], animations: {
        self.yourView.transform = CGAffineTransformMakeScale(1, 1)
    }, completion: nil)

With these lines you can add the constraints that you want on your UIView and place it where you need in your ViewController.

查看更多
登录 后发表回答