title: UITableView with UITableViewCells + AutoLayout - Not As Smooth As It *Should* Be
body: <p>I recently posted a question about a UITableView with custom UITableCells that was not smooth when the cell's subviews were positioned with AutoLayout. I got some comments suggesting the lack of smoothness was due to the complex layout of the cells. While I agree that the more complex the cell layout, the more calculation the tableView has to do to get the cell's height, I don't think 10-12 UIView and UILabel subviews should cause the amount of lag I was seeing as I scrolled on an iPad.</p>
  
  <p>So to prove my point further, I created a single UIViewController project with a single UITableView subview and custom UITableViewCells with only 2 labels inside of their subclass. And the scrolling is still not perfectly smooth. From my perspective, this is the most basic you can get - so if a UITableView is still not performant with this design, I must be missing something.</p>
  
  <p>The <code>estimatedRowHeight</code> of 110 used below is a very close estimate to the actual row height average. When I used the 'User Interface Inspector' and looked at the heights of each cell, one by one, they ranged from 103 - 124.</p>
  
  <p>Keep in mind, when I switch the code below to <em>not</em> use an <code>estimatedRowHeight</code> and <code>UITableViewAutomaticDimension</code> and instead implement <code>func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -&gt; CGFloat {</code>, calculating the height with frame values, the UITableView scrolls like butter. </p>
  
  <p><strong>Screenshot of App (for reference)</strong></p>
  
  <p><a href="https://i.stack.imgur.com/6WDOt.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/6WDOt.png" alt="enter image description here"></a></p>
  
  <p><strong>Source code of the App (where the scrolling is not perfectly smooth)</strong></p>
  
  <pre><code>// The custom `Quote` object that holds the
  // properties for our data mdoel
  class Quote {
      var text: String!
      var author: String!
  
      init(text: String, author: String) {
          self.text = text
          self.author = author
      }
  }
  
  
  // Custom UITableView Cell, using AutoLayout to
  // position both a "labelText" (the quote itself)
  // and "labelAuthor" (the author's name) label
  class CellQuote: UITableViewCell {
      private var containerView: UIView!
      private var labelText: UILabel!
      private var labelAuthor: UILabel!
  
  
      override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
          super.init(style: style, reuseIdentifier: reuseIdentifier)
  
          contentView.backgroundColor = UIColor.whiteColor()
  
          containerView = UIView()
          containerView.backgroundColor = UIColor(
              red: 237/255,
              green: 237/255,
              blue: 237/255,
              alpha: 1.0
          )
          contentView.addSubview(containerView)
          containerView.translatesAutoresizingMaskIntoConstraints = false
          containerView.leadingAnchor.constraintEqualToAnchor(contentView.leadingAnchor, constant: 0).active = true
          containerView.trailingAnchor.constraintEqualToAnchor(contentView.trailingAnchor, constant: 0).active = true
          containerView.topAnchor.constraintEqualToAnchor(contentView.topAnchor, constant: 4).active = true
          containerView.bottomAnchor.constraintEqualToAnchor(contentView.bottomAnchor, constant: 0).active = true
  
          labelText = UILabel()
          labelText.numberOfLines = 0
          labelText.font = UIFont.systemFontOfSize(18)
          labelText.textColor = UIColor.darkGrayColor()
  
          containerView.addSubview(labelText)
          labelText.translatesAutoresizingMaskIntoConstraints = false
          labelText.leadingAnchor.constraintEqualToAnchor(containerView.leadingAnchor, constant: 20).active = true
          labelText.topAnchor.constraintEqualToAnchor(containerView.topAnchor, constant: 20).active = true
          labelText.trailingAnchor.constraintEqualToAnchor(containerView.trailingAnchor, constant: -20).active = true
  
          labelAuthor = UILabel()
          labelAuthor.numberOfLines = 0
          labelAuthor.font = UIFont.boldSystemFontOfSize(18)
          labelAuthor.textColor = UIColor.blackColor()
  
          containerView.addSubview(labelAuthor)
          labelAuthor.translatesAutoresizingMaskIntoConstraints = false
          labelAuthor.topAnchor.constraintEqualToAnchor(labelText.bottomAnchor, constant: 20).active = true
          labelAuthor.leadingAnchor.constraintEqualToAnchor(containerView.leadingAnchor, constant: 20).active = true
          labelAuthor.trailingAnchor.constraintEqualToAnchor(containerView.trailingAnchor, constant: -20).active = true
          labelAuthor.bottomAnchor.constraintEqualToAnchor(containerView.bottomAnchor, constant: -20).active = true
  
          self.selectionStyle = UITableViewCellSelectionStyle.None
      }
  
      func configureWithData(quote: Quote) {
          labelText.text = quote.text
          labelAuthor.text = quote.author
      }
  
      required init?(coder aDecoder: NSCoder) {
          fatalError("init(coder:) has not been implemented")
      }
  
  }
  
  // The UIViewController that is a 
  class ViewController: UIViewController, UITableViewDataSource {
  
      var tableView: UITableView!
      var dataItems: [Quote]!
  
      override func viewDidLoad() {
          super.viewDidLoad()
  
          tableView = UITableView()
          tableView.dataSource = self
  
          tableView.registerClass(CellQuote.self, forCellReuseIdentifier: "cellQuoteId")
  
          tableView.backgroundColor = UIColor.whiteColor()
          tableView.separatorStyle = UITableViewCellSeparatorStyle.None
          tableView.estimatedRowHeight = 110
          tableView.rowHeight = UITableViewAutomaticDimension
  
          view.addSubview(tableView)
          tableView.translatesAutoresizingMaskIntoConstraints = false
          tableView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
          tableView.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 20).active = true
          tableView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true
          tableView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor).active = true
  
          dataItems = [
              Quote(text: "One kernel is felt in a hogshead; one drop of water helps to swell the ocean; a spark of fire helps to give light to the world. None are too small, too feeble, too poor to be of service. Think of this and act.", author: "Michael.Frederick"),
              Quote(text: "A timid person is frightened before a danger, a coward during the time, and a courageous person afterward.", author: "Lorem.Approbantibus."),
              Quote(text: "There is only one way to defeat the enemy, and that is to write as well as one can. The best argument is an undeniably good book.", author: "Lorem.Fruitur."),
              // ... many more quotes ...
          ]
  
      }
  
      override func didReceiveMemoryWarning() {
          super.didReceiveMemoryWarning()
      }
  
      // MARK: - UITableViewDataSource
  
      func numberOfSectionsInTableView(tableView: UITableView) -&gt; Int {
          return 1
      }
  
      func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
          return dataItems.count
      }
  
      func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&gt; UITableViewCell {
          let cell = tableView.dequeueReusableCellWithIdentifier("cellQuoteId") as! CellQuote
          cell.configureWithData(dataItems[indexPath.row])
          return cell
      }
  }
  </code></pre>
  
  <p>I like matt's suggestion below, but am still trying to tweak it to work for me:</p>
  
  <pre><code>class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
  
      var cellHeights: [CGFloat] = [CGFloat]()
  
      func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -&gt; CGFloat {
          if cellHeights.count == 0 {
              var cellHeights = [CGFloat]()
              let numQuotes: Int = dataItems.count
  
              for index in 0...numQuotes - 1 {
                  let cell = CellQuote()
                  let quote = dataItems[index]
  
                  cell.configureWithData(quote)
                  let size = cell.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
                  cellHeights.append(size.height)
              }
  
              self.cellHeights = cellHeights
          }
  
          return self.cellHeights[indexPath.row]
  
      }
  }
  </code></pre>