This is one of my notes on learning iOS Animations by Tutorials. The code in detail on andyRon/LearniOSAnimations my lot.

Auto Layout, first introduced in iOS 6, has been around for a while, with a series of successful iterations each time a new version of iOS and Xcode is released.

The core idea behind automatic layout is simple: it allows you to define the layout of your application’s UI elements based on the relationships created between each element in the layout.

We’ve used automatic layouts for static layouts in normal development, and in this article we’ll learn how to use constraints to set up animations.

6- Introduction to automatic layout

This chapter uses automatic layout to complete the Packing List of items needed in the next chapter. For automatic Layout, please refer to my previous article to start iOS 10-3 with Swift to introduce Auto Layout, which will not be repeated here.

7- Constraint animation

Animating Constraints is no more difficult than Animating properties; It’s just a little different. Typically, you just replace the existing constraint with the new one, and let Auto Layout animate the UI between the two states.

Set constraint animation

The Packing List for the starting project is roughly as follows:

Navigation bar height changes

Add constraint interface to ViewController:

@IBOutlet weak var menuHeightConstraint: NSLayoutConstraint!
Copy the code

And associate it with the height constraint of the navigation bar view:

Add the following to the Action method actionToggleMenu() in the upper right corner of the plus button:

isMenuOpen = ! isMenuOpen menuHeightConstraint.constant = isMenuOpen ? 200.0:60.0 titlelabel. text = isMenuOpen? "Select Item" : "Packing List"Copy the code

After clicking the plus button, the height of the navigation bar increases and the title changes.

Animation of layout changes

Continue adding the spring animation for layout changes in actionToggleMenu() :

UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10.0, options: .curveEaseIn, animations: {
    // Force a layout update
    self.view.layoutIfNeeded()
}, completion: nil)
Copy the code

In menuHeightConstraint. Constant = isMenuOpen? 200.0:60.0 has updated the constraint values, but iOS hasn’t had a chance to update the layout yet. By calling layoutIfNeeded() from an animation closure to force an update of the layout, you can set the center and boundaries of each view involved in the layout. For example, the table view also shrinks or increases as the Menu shrinks or increases. This is the effect of the constraint, which is now equivalent to setting two animations at once 😊.

Effect:

rotating

Rotate + 45° to x in the animation closure above:

let angle: CGFloat = self.isMenuOpen ? .pi/4 : 0.0
self.buttonMenu.transform = CGAffineTransform(rotationAngle: angle)
Copy the code

Check constraints

It is relatively easy to add code outlets to view constraints directly and visually. When it is not convenient to add interfaces in The Interfa Builder control-drag mode or to add too many outlets, you can use the constraints property provided by UIView, which is an array of all the constraints of the current view.

For example:

titleLabel.superview? .constraints.forEach { constraintin
    print("- >\(constraint.description)\n")}Copy the code

Print result:

-> <NSLayoutConstraint:0x600002d04320 UIView:0x7ff7df530c00.height == 200   (active)>

-> <NSLayoutConstraint:0x600002d02210 UILabel:0x7ff7df525350'Select Item'.centerX == UIView:0x7ff7df530c00.centerX   (active)>

-> <NSLayoutConstraint:0x600002d02a30 UILabel:0x7ff7df525350'Select Item'.centerY == UIView:0x7ff7df530c00.centerY + 5   (active)>

-> <NSLayoutConstraint:0x600002d02d00 H: [UIButton:0x7ff7df715d20'+'] - (8)-|   (active, names: '|':UIView:0x7ff7df530c00) > - > <NSLayoutConstraint:0x600002d030c0 UIButton:0x7ff7df715d20'+'.centerY == UILabel:0x7ff7df525350'Select Item'.centerY   (active)>
Copy the code

It looks a bit messy, but if you look closely you can see that there are five constraints that correspond to:

Set the constraint animation for UILabel

IsMenuOpen =! In actionToggleMenu() Add under isMenuOpen:

titleLabel.superview? .constraints.forEach { constraintin
    if constraint.firstItem === titleLabel && constraint.firstAttribute == .centerX {
        constraint.constant = isMenuOpen ? -100.0 : 0.0
        return}}Copy the code

The general form of a constraint expression is as follows:

firstItem.firstItemAttribute == secondItem.secondItemAttribute * multiplier + constant
Copy the code

The various attributes that correspond to NSLayoutConstraint have obvious names, where == corresponds to relation, and can also be <=, >=, etc.

Practical examples:

Superview.CenterX = 1.0 * UILabel.CenterX + 0.0
Copy the code

Here’s the effect:

Alternative constraint

Each constraint can have an Identifier attribute added to it, and the constraint can be retrieved from the Identifier in the code.

Continue with the above constraint and add:

if constraint.identifier == "TitleCenterY" {
    constraint.isActive = false
    let newConstraint = NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: titleLabel.superview! , attribute: .centerY, multiplier: isMenuOpen ?0.67 : 1.0, constant: 5.0)
    newConstraint.identifier = "TitleCenterY"
    newConstraint.isActive = true
    return
}
Copy the code

CenterY = menu. CenterY * 0.67 + 0.0.

Effect after operation:

Add navigation bar content

In actionToggleMenu() add:

if isMenuOpen {
    slider = HorizontalItemList(inView: view)
    slider.didSelectItem = { index in
                            print("add \(index)")
                            self.items.append(index)
                            self.tableView.reloadData()
                            self.actionToggleMenu(self)}self.titleLabel.superview! .addSubview(slider) }else {
    slider.removeFromSuperview()
}
Copy the code

HorizontalItemList is a custom UIScrollView subclass for left and right scrolling views in menu,

Dynamically creating views

When the TableView cell is clicked, showItem(_:) is called and added to this method:

// Click to create an image
let imageView = UIImageView(image: UIImage(named: "summericons_100px_0\(index).png"))
imageView.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
imageView.layer.cornerRadius = 5.0
imageView.layer.masksToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imageView)
Copy the code

Add constraint code:

let conx = imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
Copy the code

This method uses the new NSLayoutAnchor class, which makes it easy to create common constraints. Here, you will create constraints between the center X anchor of the image view and the view controller’s view.

Add a constraint at the bottom of the image:

let conBottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: imageView.frame.height)
Copy the code

This constraint sets the bottom of the image view to match the bottom of the view controller view, plus the image height; This positions the image beyond the bottom edge of the screen, which will be the starting point for the animation.

Add image width constraint:

let conWidth = imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.33, constant: -50.0)
Copy the code

This sets the image width to 1/3 of the screen width minus 50 LBS. The target size is 1/3 of the screen; You will animate 50 pounds of difference so that the image “grows” into place.

Finally, add the height and width equality constraint and activate all of the above constraints:

let conHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)
NSLayoutConstraint.activate([conx, conBottom, conWidth, conHeight])
Copy the code

Click on the TableView Cell and see the following:

Create animations for dynamically created views

In showItem(_:) add:

UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.0, animations: {
    conBottom.constant = -imageView.frame.size.height/2
    conWidth.constant = 0.0
    self.view.layoutIfNeeded()
}, completion: nil)
Copy the code

But here’s the effect:

** Think about it: ** adds a view, sets some constraints, then changes those constraints and animates layout changes. However, the view never has a chance to perform its initial layout, so the image starts at its default position (0, 0) in the upper left corner 🙄.

To fix this, just do the initial layout before the animation starts, and add before the animation:

view.layoutIfNeeded()
Copy the code

The effect is to come up from below:

Remove the image that already appears

The images that pop up above will overlap and need to be removed before the next image comes out.

Add below the previous code:

UIView.animate(withDuration: 0.8, delay: 1.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.0, animations: {
            conBottom.constant = imageView.frame.size.height
            conWidth.constant = -50.0
            self.view.layoutIfNeeded()
        }) { (_) in
            imageView.removeFromSuperview()
        }
Copy the code

The result is 😝

This article is in my personal blog address: System learning iOS animation ii: Automatic layout animation