4

My UITableView is scrolling up once I add a new comment to CoreData. NSFetchedResultsController knows about it, puts this comment at the bottom of the table, and scroll table to top. Is it normal?

My example:

Just before I add a comment:

enter image description here

Just after I add a comment (not expected behavior):

enter image description here

It should be just like this (after I swipe it by hand manually):

enter image description here

This may be connected with following situation:

  1. This is sort descriptor of my NSFetchedResultsController:

    NSSortDescriptor(key: "createdAt", ascending: false) //from the latest to the oldest
    
  2. But I need to display my comments for reversed index paths (the latest are at the very bottom). In other words, everywhere when I need to use indexPath I use:

    private func reversedIndexPathForIndexPath(indexPath: NSIndexPath) -> NSIndexPath {
        return NSIndexPath(forRow: fetchedResultsController.fetchedObjects!.count - indexPath.row - 1, inSection: 0)
    }
    

NSFetchedResultsControllerDelegate:

//MARK: - NSFetchedResultsControllerDelegate

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    self.tableView.beginUpdates()
}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    switch type {
    case .Insert:
        if let newIndexPath = newIndexPath {
            tableView.insertRowsAtIndexPaths([reversedIndexPathForIndexPath(newIndexPath)], withRowAnimation: .Fade)
        }
    case .Delete:
        if let indexPath = indexPath {
            tableView.deleteRowsAtIndexPaths([reversedIndexPathForIndexPath(indexPath)], withRowAnimation: .Fade)
        }
    case .Update:
        if let indexPath = indexPath {
            tableView.reloadRowsAtIndexPaths([reversedIndexPathForIndexPath(indexPath)], withRowAnimation: .Fade)
        }
    case .Move:
        if let indexPath = indexPath, let newIndexPath = newIndexPath {
            tableView.deleteRowsAtIndexPaths([reversedIndexPathForIndexPath(indexPath)], withRowAnimation: .Fade)
            tableView.insertRowsAtIndexPaths([reversedIndexPathForIndexPath(newIndexPath)], withRowAnimation: .Fade)
        }
    }
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    tableView.endUpdates()
}

Is it connected to my problem?

Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358

2 Answers2

7

Edited answer:

I was able to reproduce this glitch with the following steps:

  1. UITableView with its rowHeight set to UITableViewAutomaticDimension and non-zero estimatedRowHeight.
  2. Table scrolled to its very bottom, with the last cell fully visible at the bottom margin.
  3. Inserting the new cell below the last one results in visual shift of the cells a few points down (in my setup it differs from 5 to 40).

The source of this behavior needs additional investigation.

For now the only solution is not to use UITableViewAutomaticDimension and return row heights from tableView:heightForRowAtIndexPath:.

By the way, implementation of inverted index paths has one significant flaw. When FRC calls it's delegate method controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:, all deletion index paths belongs to pre-change data set, and all insertion index paths - to post-change one. When you are in-beetween controllerWillChangeContent: and controllerDidChangeContent:, fetchedResultsController.fetchedObjects will contain post-change data set, i suppose (needs to be checked, though).

Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
bteapot
  • 1,897
  • 16
  • 24
3

I found something that worked for me. I encountered the same problem, and my estimatedRowHeight was 33 and all of my cells are greater than 33.

I just changed my estimatedRowHeight to my maximum cell height(or greater than your maximum cell height)

    self.table.estimatedRowHeight = 310
    self.table.rowHeight = UITableViewAutomaticDimension

worked like a charm, all cells are now autoresizing and uitableview is not scrolling to top or bottom when core data performs an update/delete/insert