Home iOS & Swift Books Auto Layout by Tutorials

9
Animating Auto Layout Constraints Written by Libranner Santos

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

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

So far, you’ve been creating static constraints and learning how to structure your app’s UI using Auto Layout. But did you know that you can also animate your constraints? It’s true — and in this chapter, you’ll learn how to do it.

With animations, you can:

  • Give feedback to the users.
  • Direct the attention to a specific part of the app.
  • Help the user to identify connections between parts of the app.
  • Create a better look and feel.
  • Improve navigation.

When done properly, animations can increase user engagement and are often what makes (or breaks) your app’s success. When creating animations, you need to consider three things: start value, end value and time.

Animate Auto Layout using Core Animations

You can animate constraints either by changing the constant of a constraint or by activating and deactivating constraints. The method you choose depends largely on what you’re trying to accomplish.

In this section, you’ll play with constraint animations by updating the MessagingApp project. Specifically, you’ll add new functionality to the app that allows users to like messages and mark them as favorite. Here’s how it’ll work: When a user double-taps a message, the app will show a toolbar with buttons to like the messages and to mark the message as favorite. This functionality will only work for messages not sent by the user.

To begin, open the MessagingApp project and build and run it.

The app displays a chat between two users; the messages on the right are the ones sent by you. If you double-tap one of the blue bubble messages now, nothing happens. But that’s about to change!

Setting up the delegate for MessageBubbleTableViewCell

Open MessageBubbleTableViewCell.swift and add the following code above the class declaration:

protocol MessageBubbleTableViewCellDelegate {
  func doubleTapForCell(_ cell: MessageBubbleTableViewCell)
}
var delegate: MessageBubbleTableViewCellDelegate?
@objc func doubleTapped() {
    delegate?.doubleTapForCell(self)
 }
let gesture = UITapGestureRecognizer(
  target: self, 
  action: #selector(doubleTapped))
gesture.numberOfTapsRequired = 2
gesture.cancelsTouchesInView = true
contentView.addGestureRecognizer(gesture)
private let toolbarView = ToolbarView()
private var toolbarViewTopConstraint: NSLayoutConstraint!
private func setupToolbarView() {
  //1
  view.addSubview(toolbarView)

  //2
  toolbarViewTopConstraint = 
    toolbarView.topAnchor.constraint(
      equalTo: view.safeAreaLayoutGuide.topAnchor, 
      constant: -100)

  toolbarViewTopConstraint.isActive = true

  //3  
  toolbarView.leadingAnchor.constraint(
    equalTo: view.safeAreaLayoutGuide.leadingAnchor, 
    constant: 30).isActive = true
}
setupToolbarView()
cell.delegate = self
extension MessagesViewController: MessageBubbleTableViewCellDelegate {
  func doubleTapForCell(_ cell: MessageBubbleTableViewCell) {
    //1
    guard let indexPath = self.tableView.indexPath(for: cell)
      else { return }
    let message = messages[indexPath.row]
    guard message.sentByMe == false else { return }

    //2
    toolbarViewTopConstraint.constant = cell.frame.midY

    //3
    toolbarView.alpha = 0.95

    //4
    toolbarView.update(
      isLiked: message.isLiked, 
      isFavorited: message.isFavorited)
    
    //5
    toolbarView.tag = indexPath.row

    //6
    UIView.animate(
      withDuration: 1.0,
      delay: 0.0,
      usingSpringWithDamping: 0.6,
      initialSpringVelocity: 1,
      options: [],
      animations: {
        self.view.layoutIfNeeded()
      },
      completion: nil)
  }
}

@objc func hideToolbarView() {
  //1
  self.toolbarViewTopConstraint.constant =  -100
  
  //2
  UIView.animate(
    withDuration: 1.0,
    delay: 0.0,
    usingSpringWithDamping: 0.6,
    initialSpringVelocity: 1,
    options: [],
    animations: {
      self.toolbarView.alpha = 0
      self.view.layoutIfNeeded()
  },
  completion: nil)
}
let gesture = UITapGestureRecognizer(
  target: self, 
  action: #selector(hideToolbarView))
gesture.numberOfTapsRequired = 1;
gesture.delegate = self
tableView.addGestureRecognizer(gesture)
extension MessagesViewController: UIGestureRecognizerDelegate {
  func gestureRecognizer(
    _ gestureRecognizer: UIGestureRecognizer, 
    shouldReceive touch: UITouch
  ) -> Bool {
    return touch.view == tableView
  }
}

toolbarView.delegate = self
extension MessagesViewController: ToolbarViewDelegate {
  func toolbarView(
    _ toolbarView: ToolbarView, 
    didFavoritedWith tag: Int
  ) {
    messages[tag].isFavorited.toggle()
  }
  
  func toolbarView(
    _ toolbarView: ToolbarView, 
    didLikedWith tag: Int
  ) {
    messages[tag].isLiked.toggle()
  }
}

Key points

  • Remember, you can activate and deactivate constraints to create animations.
  • Use animations to create a more engaging user experience.
  • To force Auto Layout to satisfy the new constraints, call layoutIfNeeded() on the affected view.

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.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

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 raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.