title: TableView gets laggy with data
body: <p>So I am facing a problem</p>
  
  <p>A table view gets slower (responses slower to scrolling, tapping) after reloading/adding rows for a couple of times</p>
  
  <hr>
  
  <p>So after the user logs in, the app downloads 10 <em>"WorldMessages"</em>. It is loaded into this table view.</p>
  
  <p>If the user scrolls down, it calls a function which loads more 10: <code>loadOlderOwnWorldMessages()</code></p>
  
  <p>Each cell has a <code>tapGestureRecognizer</code> + <code>longPressGestureRecognizer</code></p>
  
  <p>And I have to mention that If the user reloads the tableView, then it clears the data and loads only the first 10 WorldMessages again</p>
  
  <hr>
  
  <p><strong>The problem</strong></p>
  
  <p>I don't know why, but for example, if I reload the tableView 50 times and every time I do scroll down a bit or more, then the tableView gets slow.</p>
  
  <p>Maybe because of the tap/long press gesture recognizers, or constraints?</p>
  
  <p><strong>The app looks like this:</strong></p>
  
  <p><a href="https://i.stack.imgur.com/xYkOf.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/xYkOf.png" alt="enter image description here"></a></p>
  
  <p><strong>Video after it gets laggy:</strong></p>
  
  <p><a href="https://www.youtube.com/watch?v=65NkjS-Kz3M" rel="nofollow noreferrer">https://www.youtube.com/watch?v=65NkjS-Kz3M</a></p>
  
  <p><em>(If I kill the app and I open it again, then it works smoothly again until I reload it a couple of times)</em></p>
  
  <p><strong>Code:</strong></p>
  
  <p>// It's not the whole code, I deleted lots of lines of it that were not important (like the reload functions, and etc</p>
  
  <pre><code>class ProfileViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITabBarDelegate {
  
      // Classes
      let handleData = HandleData()
      let handleResponses = HandleResponses()
      let worldMessagesFunctions = WorldMessagesFunctions()
      let profileFunctions = ProfileFunctions()
  
      // View Objects
      @IBOutlet var tableView : UITableView!
  
      // Variables
      var userWorldMessages = [WorldMessage]()
      var lastContentOffsetY : CGFloat?
  
      var currentSelectedWorldMessageIndexPath : IndexPath?
  
      // Main Code
      override func viewDidLoad() {
          super.viewDidLoad()
  
          userWorldMessages = ownWorldMessages.shared.worldMessages
      }
  
      func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
         return userWorldMessages.count
      }
      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
  let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileWorldMessageCell", for: indexPath) as! ProfileWorldMessageCell
  
          let worldMessage = userWorldMessages[indexPath.row]
  
          cell.worldMessageData = worldMessage
  
          cell.messageLabel.text = worldMessage.message
  
          if let image = UIImage(named: "bubble") {
              let h = image.size.height / 2
              let w = image.size.width / 2
              cell.bubbleImageView.image = image
                  .resizableImage(withCapInsets:
                      UIEdgeInsetsMake(h, w, h, w),
                                  resizingMode: .stretch).withRenderingMode(.alwaysTemplate)
  
              cell.bubbleImageView.tintColor = appColors.worldMessageBubble
  
          }
  
          let calendar = NSCalendar.current
          let date = Date(timeIntervalSince1970: Double(worldMessage.leftTime))
          let dateFormatter = DateFormatter()
          dateFormatter.locale = Locale(identifier: "en_US")
          var dateFormat = "yyyy MMM dd"
          if calendar.isDateInToday(date) {
              // Today
              dateFormat = "HH:mm"
              dateFormatter.string(from: date)
          } else if (calendar.date(byAdding: .weekOfYear, value: -1, to: Date())! &lt; date){
              // This last 7 days
              dateFormat = "EEEE HH:mm"
          } else if (calendar.date(byAdding: .month, value: -12, to: Date())! &lt; date){
              // This year
              dateFormat = "MMM dd"
          } else {
              dateFormat = "yyyy MMM dd"
          }
          dateFormatter.dateFormat = dateFormat
          let strDate = dateFormatter.string(from: date)
  
          cell.timeLabel.text = strDate
  
          cell.commentsButton.setTitle("\(worldMessage.comments!) comments", for: .normal)
          cell.likesButton.setTitle("\(worldMessage.likes!) likes", for: .normal)
  
          // tapped
          let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(bubbleTappedHandler))
          cell.bubbleButton.addGestureRecognizer(tapGestureRecognizer)
          cell.bubbleButton.isUserInteractionEnabled = true
  
          // long pressed
          let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(bubbleLongPressHandler))
          longPressGestureRecognizer.minimumPressDuration = 0.5
          cell.addGestureRecognizer(longPressGestureRecognizer)
          cell.isUserInteractionEnabled = true
  
          return cell
      }
      var cellHeights: [IndexPath : CGFloat] = [:]
      func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
          cellHeights[indexPath] = cell.frame.size.height
  
          if indexPath.row == UserWorldMessagesStore.shared.worldMessages.count - 1 &amp;&amp; userWorldMessagesCanLoadMore == true {
              loadOlderOwnWorldMessages()
          }
      }
      func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -&gt; CGFloat {
          return UITableViewAutomaticDimension
      }
      func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -&gt; CGFloat {
          if cellHeights[indexPath] != nil {
              return CGFloat(Float(cellHeights[indexPath] ?? 0.0))
          }
          else {
              return UITableViewAutomaticDimension
          }
      }
      func scrollViewDidScroll(_ scrollView: UIScrollView) {
          if (lastContentOffsetY != nil){
              tableView.setContentOffset(CGPoint(x: 0, y: lastContentOffsetY!), animated: false)
          }
      }
  
  
  
      func loadOlderOwnWorldMessages(){
          profileFunctions.loadOlderOwnWorldMessages(startIndex: (userWorldMessages.count)) { response, newUserWorldMessages in
              if let response = response {
                  if response.type == 1 {
                      DispatchQueue.main.async(execute: {() -&gt; Void in
                          if newUserWorldMessages != nil {
  
                              var insertRows : [IndexPath] = []
                              var fromIndex = UserWorldMessagesStore.shared.worldMessages.count - 1
                              for worldMessage in newUserWorldMessages! {
                                  UserWorldMessagesStore.shared.worldMessages.append(worldMessage)
                                  insertRows.append(IndexPath(row: fromIndex, section: 1))
                                  fromIndex += 1
                              }
                              self.userWorldMessages = UserWorldMessagesStore.shared.worldMessages
  
                              self.tableView.beginUpdates()
                              self.tableView.insertRows(at: insertRows, with: .automatic)
                              self.tableView.endUpdates()
  
  
                          }
  
                          })
                  } else {
                      DispatchQueue.main.async(execute: {() -&gt; Void in
                          self.handleResponses.displayError(title: response.title, message: response.message)
                      })
                  }
              }
          }
      }
  
      @objc func bubbleTappedHandler(sender: UITapGestureRecognizer, should: Bool) {
          let touchPoint = sender.location(in: self.tableView)
          if let indexPath = tableView.indexPathForRow(at: touchPoint) {
              if indexPath == currentSelectedWorldMessageIndexPath {
                  // it was already selected, so deselect it
  
                  deselectCurrentSelectedWorldMessage()
              } else {
                  // select new one, deselect old one if was selected
  
                  if (currentSelectedWorldMessageIndexPath != nil){
                      deselectCurrentSelectedWorldMessage()
                  }
  
                  selectWorldMessage(indexPath: indexPath)
              }
          }
      }
      func selectWorldMessage(indexPath: IndexPath) {
  
          tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
          currentSelectedWorldMessageIndexPath = indexPath
  
          if let cell = tableView.cellForRow(at: indexPath) as? ProfileWorldMessageCell {
  
              // Change some constraints
              cell.messageLabelTopConstraint.constant = 14
              cell.messageLabelBottomConstraint.constant = 14
              UIView.animate(withDuration: 0.15, animations: {
                  self.view.layoutIfNeeded()
                  self.tableView.beginUpdates()
                  self.tableView.endUpdates()
              }, completion: nil)
          }
      }
      @objc func deselectCurrentSelectedWorldMessage(){
          UIMenuController.shared.setMenuVisible(false, animated: true)
          if (currentSelectedWorldMessageIndexPath == nil){
              return
          }
          let indexPath = currentSelectedWorldMessageIndexPath!
  
          if let cell = tableView.cellForRow(at: indexPath) as? ProfileWorldMessageCell {
  
              // Change back some constraints
              cell.messageLabelTopConstraint.constant = 10
              cell.messageLabelBottomConstraint.constant = 10
              UIView.animate(withDuration: 0.15, animations: {
                  self.view.layoutIfNeeded()
                  self.tableView.beginUpdates()
                  self.tableView.endUpdates()
              }, completion: nil)
          }
  
          currentSelectedWorldMessageIndexPath = nil
      }
  
      @objc func bubbleLongPressHandler(sender: UILongPressGestureRecognizer, should: Bool) {
          // Show options like copy, delete etc.
      }
  
  
  
  }
  </code></pre>