title: Initial scroll lag in UITableView cells with resized large images?
body: <p>I'm basically having the same issue as this question:</p>
  
  <p><a href="https://stackoverflow.com/questions/14629769/slow-scroll-on-uitableview-images">Slow scroll on UITableView images</a></p>
  
  <p>My <code>UITableView</code> contains a <code>UIImageView</code> that is 87x123 size.</p>
  
  <p>When my <code>UITableViewController</code> is loaded, it first calls a function that loops through an array of images. These images are high resolutions stored from the photo library. In each iteration, it retrieves the image and resize each image down to 87x123, then stores it back into the original image in the array.</p>
  
  <p>When all the images has been resized and stored, it calls <code>self.tableView.reloadData</code> to populate the data in the array into the cells.</p>
  
  <p>However, like the mentioned question, my <code>UITablView</code> is choppy and lags if I scroll fast before all the images has been resized and stored in the array.</p>
  
  <p>Here's the problematic code:</p>
  
  <pre><code>extension UIImage
  {
      func resizeImage(originalImage: UIImage, scaledTo size: CGSize) -&gt; UIImage
      {
          // Avoid redundant drawing
          if originalImage.size.equalTo(size)
          {
              return originalImage
          }
  
          UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
          originalImage.draw(in: CGRect(x: CGFloat(0.0), y: CGFloat(0.0), width: CGFloat(size.width), height: CGFloat(size.height)))
  
          let image = UIGraphicsGetImageFromCurrentImageContext()!
          UIGraphicsEndImageContext()
  
          return image
      }
  }
  
  func loadImages()
  {   
      DispatchQueue.global(qos: .background).async {
  
          for index in 0..&lt;self.myArray.count
          {
              if let image = self.myArray[index].image
              {
                  self.myArray[index].image = image.resizeImage(originalImage: image, scaledTo: CGSize(width: 87, height: 123) )
              }
  
              if index == self.myArray.count - 1
              {
                  print("FINISHED RESIZING ALL IMAGES")
              }
          }
      }
  
      tableView.reloadData()
  }
  
  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell
  {
      ...
  
      // Size is 87x123
      let thumbnailImage = cell.viewWithTag(1) as! UIImageView
  
      DispatchQueue.main.async{
  
          thumbnailImage.image = self.myArray[indexPath.row]
  
      }
  
      thumbnailImage.contentMode = UIViewContentMode.scaleAspectFill
  
      thumbnailImage.layer.borderColor = UIColor.black.cgColor
      thumbnailImage.layer.borderWidth = 1.0
      thumbnailImage.clipsToBounds = true
  
      return cell
  }
  </code></pre>
  
  <p>I know to do any Non-UI operations in the background thread, which is what I do. Then all I do in <code>cellForRowAt</code> is load the image into the cell using its <code>indexPath.row</code>.</p>
  
  <p>The problem is, as previously mentioned, if I start to scroll the <code>UITableView</code> <strong>BEFORE</strong> <code>FINISHED RESIZING ALL IMAGES</code> is printed out, i.e. before all the images has been resized, there is noticeable lag and slowness.</p>
  
  <p>However, if I wait UNTIL all the images has been resized and <code>FINISHED RESIZING ALL IMAGES</code> is called before scrolling the <code>UITableView</code>, the scrolling is smooth without any lags.</p>
  
  <p>I can put a loading indicator and have the user wait until all images has been resized and loaded into the cells before having user interaction, but that would be an annoyance since it takes about 8 seconds to resize all the high-res images (18 images to resize).</p>
  
  <p>Is there a better way I can fix this lag?</p>
  
  <p><strong>UPDATE</strong>: Following @iWheelBuy's second example, I've implemented the following:</p>
  
  <pre><code>final class ResizeOperation: Operation {
  
      private(set) var image: UIImage
      let index: Int
      let size: CGSize
  
      init(image: UIImage, index: Int, size: CGSize) {
          self.image = image
          self.index = index
          self.size = size
          super.init()
      }
  
      override func main() {
          image = image.resizeImage(originalImage: image, scaledTo: size)
      }
  }
  
  class MyTableViewController: UITableViewController
  {
      ...
  
      lazy var resizeQueue: OperationQueue = self.getQueue()
  
      var myArray: [Information] = []
  
      internal struct Information
      {
          var title: String?
          var image: UIImage?
  
          init()
          {
  
          }
      }
  
      override func viewDidLoad()
      {
          ....
  
          loadImages()
  
          ...
      }
  
      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell
      {
          ...
  
          // Size is 87x123
          let thumbnailImage = cell.viewWithTag(1) as! UIImageView
  
          DispatchQueue.main.async{
  
              thumbnailImage.image = self.myArray[indexPath.row].image
  
          }
  
          thumbnailImage.contentMode = UIViewContentMode.scaleAspectFill
  
          return cell
      }
  
      func loadImages()
      {
          let size = CGSize(width: 87, height: 123)
  
          for item in myArray
          {
              let operations = self.myArray.enumerated().map({ ResizeOperation(image: item.image!, index: $0.offset, size: size) })
  
              operations.forEach { [weak queue = resizeQueue, weak controller = self] (operation) in
                  operation.completionBlock = { [operation] in
                      DispatchQueue.main.async { [image = operation.image, index = operation.index] in
  
                          self.update(image: image, index: index)
                      }
                  }
                  queue?.addOperation(operation)
              }
          }
      }
  
      func update(image: UIImage, index: Int)
      {
          myArray[index].image = image
  
          tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: UITableViewRowAnimation.fade)
  
      }    
  }
  </code></pre>
  
  <p>However, upon calling <code>tableView.reloadRows</code>, I receive a crash with the error:</p>
  
  <blockquote>
    <p>attempt to delete row 0 from section 0, but there are only 0 sections
    before the update</p>
  </blockquote>
  
  <p>I'm a bit confused on what it means and how to resolve it.</p>