Contents

Hide contents

iOS Animations by Tutorials

Section IV: Layer Animations

Section 4: 9 chapters
Show chapters Hide chapters

9 Animating Constraints
Written by Marin Todorov

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

In the previous chapter, you learned how to use Auto Layout to create a responsive user interface for your Packing List project. You’ll take it up a notch in this chapter and add a number of bouncy animations to your app. You’ve learned that in order for Auto Layout to work properly, you can’t fiddle directly with the view’s frame or center properties. Instead, you have to work with the layout constraints to create your desired animations.

So far you’ve seen how to animate view properties: you can animate numeric properties, such as alpha, from one float value to another. Instances of CGPoint, like in the case of the center property, can be modified progressively until the center value reaches the target position. Naturally, your next question would be “That’s great — but how do I animate a constraint?”

Animating constraints is no more difficult than animating properties; it’s just a little different. Usually you simply replace an existing constraint with a new one and let Auto Layout animate the UI between the two states.

The sole exception is when you only need to change a single property of the constraint, such as constant, in the constraint’s equation. In that case, you simply modify the constraint directly in code and animate the change.

In this chapter, you’ll add an animation to expand the Packing List menu bar and reveal a list of items; the user can then tap an item to add it to their packing list as shown below:

This interaction — as well as a few more visual treats — will be driven by fluid and eye-catching animations.

What are you waiting for? Time to get packing!

Animating Interface Builder Constraints

If you completed the project from the previous chapter, you can carry on where you left off; otherwise, you can use the starter project from this chapter.

Your first task is to expand the menu when the user taps the + button. In order to do that, you’ll need to change the height of the menu bar by animating its height constraint.

Making the Menu Expand

Open ViewController.swift and scroll to the top of the class. Under the rest of the outlets add the following line of code:

@IBOutlet weak var menuHeightConstraint: NSLayoutConstraint!

isMenuOpen.toggle()
menuHeightConstraint.constant = isMenuOpen ? 184.0 : 44.0
titleLabel.text = isMenuOpen ? "Select Item" : "Packing List"

Animating Layout Changes

Add the following code to the bottom of actionToggleMenu:

UIView.animate(withDuration: 1.0, delay: 0.0, 
  usingSpringWithDamping: 0.4, initialSpringVelocity: 10.0, 
  options: .curveEaseIn, 
  animations: {
    self.view.layoutIfNeeded()
  }, 
  completion: nil
)

Rotating View Animations

Since you already know how to rotate views by adjusting their transform, add the following code to the final animations closure:

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

Inspecting and Animating Constraints

Working with outlets in a visual fashion is a relatively easy way to connect up your outlets, but sometimes you can’t use Interface Builder to connect all the bits of your UI to your outlets. You might add constraints from code, or maybe you just don’t want to Control-drag and create a massive number of outlets!

titleLabel.superview?.constraints.forEach { constraint in
  print(" -> \(constraint.description)\n")
}

Animating UILabel Constraints

Find the following line near the top of actionToggleMenu(_:):

isMenuOpen = !isMenuOpen
titleLabel.superview?.constraints.forEach { constraint in
  if constraint.firstItem === titleLabel && 
    constraint.firstAttribute == .centerX {
    constraint.constant = isMenuOpen ? -100.0 : 0.0
    return
  }
}

Animating by Replacing Constraints

At this point in the chapter, you’ve only modified the constant property of your constraints. Ironically, the constant property is a mutable property in the NSLayoutConstraint class!

if constraint.identifier == "TitleCenterY" {
  constraint.isActive = false
  //add new constraint
  return
}

Adding Constraints Programmatically

When the menu is retracted you want the title vertically centered within the menu view like so:

let newConstraint = NSLayoutConstraint(
  item: titleLabel,
  attribute: .centerY,
  relatedBy: .equal,
  toItem: titleSuperview,
  attribute: .centerY,
  multiplier: isMenuOpen ? 0.67 : 1.0,
  constant: 0
)
newConstraint.identifier = "TitleCenterY"
newConstraint.isActive = true

Adding Menu Content

Your next job is to show a list of items in the menu; these are all possible items you can add to your packing list.

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

Animating Dynamically Created Views

Your ultimate task, which will use everything you’ve learned up to this point and close out the chapter nicely, will be to create a new view, add some constraints to the view and animate it on the screen.

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)
let conX = imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let conBottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: imageView.frame.height)
let conWidth = imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.33, constant: -50.0)
let conHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)

NSLayoutConstraint.activate([conX, conBottom, conWidth, conHeight])

Adding Additional Dynamic Animations

Add the following code to showItem(_:):

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
)

view.layoutIfNeeded()

Challenges

Challenge 1: Animate the Image out of the Screen

OK — now you get to fix those pesky image views that stay stuck on the screen.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2022 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.

Unlock Now