Error: UITableView jump to top with UITableViewAutomaticDimension
Asked Answered
U

7

17

I am using UITableView with estimatedRowHeight and UITableViewAutomaticDimension. Also I am using NSFetchedResultsControllerDelegate to reflect the changes.

Everything is work fine. Now the problem is when ever I add some record to CoreData, NSFetchedResultsController called the it's delegate but an unexpected things happen. TableView suddenly scroll to Top every time.

NSFetchedResultsControllerDelegate

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

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

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
        switch type {
        case .Insert:
            tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .None)
            break

        case .Update:
            tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .None)
            break

        case .Delete:
            tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .None)
            break

        default: break;
        }
    }

By googling I found few answers where people suggested to use tableView: heightForRowAtIndexPath: but as my cell height is dynamic. So what should I do?

Unswerving answered 3/5, 2016 at 10:29 Comment(6)
I've already answered the similar question some time ago: https://mcmap.net/q/746027/-uitableview-powered-by-fetchedresultscontroller-with-uitableviewautomaticdimension-cells-move-when-table-is-reloadedAndaman
Try to implement the methods, - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self.tableView beginUpdates]; } and - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; }Claxton
@SanuS thanks for your suggestion. It was already there.Unswerving
Did you find a solution?Plunk
@Plunk not yet. :(Unswerving
@Plunk for scroll at particular cell use my code . its perfectly work.Gainly
H
7

This is default behaviour of tableview, inserting an row in tableview scroll it to top. I did faced the same issue with my app. Here is how i able to manage the content offset of tableview, follow the steps,

1)Store the content offset of UITableview before inserting row or section.

2)Insert objects in array.

3)Reload the tableview

4)Subtract the difference and set the content offset manually.

let oldOffset: CGFloat = tblTest.contentSize.height
tblTest.reloadData()
let newOffset: CGFloat = tblTest.contentSize.height
tblTest.setContentOffset(CGPointMake(0, (newOffset - oldOffset)), animated: false)

This is how i have done in my applications so far, and with dynamic cell to overcome the reinitialise cell everytime, it is working just awesome.

Hintze answered 14/10, 2016 at 12:15 Comment(4)
did this not helped @Tapas palHintze
It's work partially.. but not the way I want. Reloading table view looses the insert animation and also as tableview cell contains some video and images so reloading table view create a blink effect.Unswerving
no they will not, it just matter of how you refuse them. Don't initialise cell every time it loads.Hintze
i am using this technique with chat app with media and texts, work as like load more works in whatsapp.Hintze
B
4

Adding to @jayesh-miruliya 's answer, in your NSFetchedResultsControllerDelegate after the switch statement, put this in your code:

tableView.reloadData()
dispatch_async(dispatch_get_main_queue(), {
    let topCellIndexPath = NSIndexPath(forRow: 0, inSection: 0)
    self. tableView.scrollToRowAtIndexPath(topCellIndexPath, atScrollPosition: .Top, animated: true)
})
Bradybradycardia answered 4/5, 2016 at 7:11 Comment(4)
Yes I have, you should put UI related codes in Main Queue, and by using animated: true, instead of suddenly going to the top, it'll animate it. You can use ".Bottom" as well, inside atScrollPosition.Bradybradycardia
why don't you add some explanations? your answer has extremely low qualityTug
I think there is some misunderstanding happen. My intension is not to scroll the table view to TOP or BOTTOM. When a new cell is being inserted table view should not scroll up or down automatically.Unswerving
@TapasPal I've done a similar thing in my project, and deleteAtIndexPaths works properly without scrolling the tableView. When the tableView scrollsToTop, it empties the table and re-inserts the data? Do you reload the table anywhere else in your code? I assume some other methods/callbacks are causing this issue.Bradybradycardia
P
4

To solve my problem I save the cell height in the willDisplay method and in estimatedHeightForRowAt I'm retrieving the value.

var cellHeights = NSMutableDictionary()

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    if let height = cellHeights.object(forKey: indexPath) {
        return height as! CGFloat
    }

    return UITableViewAutomaticDimension
}

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeights.setObject(cell.frame.size.height, forKey: indexPath as NSCopying)
}
Plunk answered 13/10, 2016 at 9:52 Comment(2)
Though I didn't try but doubt that it'll solve the jumping issue.Unswerving
great answer! Helped me after 3 days of lurkingJounce
D
1
tableView.reloadData()
if tableView.numberOfRowsInSection(0) > 0
{
      let indexPath = NSIndexPath(forRow: 0, inSection: 0)
      self. tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Top, animated: true)
}
Dionysus answered 3/5, 2016 at 10:33 Comment(4)
Where should I add this?Unswerving
why don't you add some explanations? your answer has extremely low qualityTug
I think there is some misunderstanding happen. My intension is not to scroll the table view to TOP or BOTTOM. When a new cell is being inserted table view should not scroll up or down automatically.Unswerving
i still don't get why you're not putting some explanation to your answer. just post code is extremely low in quality of your answerTug
E
1
  func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
    switch type {
    case .Insert:

      tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .None)
        break

    case .Update:
        tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .None)
        break

    case .Delete:
        tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .None)
        break

    default: break;
    }
   tableView.reloadData()
  let indexPath = NSIndexPath(forRow: 0, inSection: 0)
  self. tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Top, animated: true)
   }

}
Eluvium answered 9/5, 2016 at 10:15 Comment(3)
If I call reloadData() method, it'll remove all the animations right?Unswerving
Also as you set the scroll position to indexPath that means it'll always try to focus at bottom most cell.Unswerving
why don't you add some explanations? your answer has extremely low qualityTug
G
-1
let cou : Int = self.Array.count as Int
let lastindex : NSIndexPath = NSIndexPath(forRow: cou-1, inSection: 0)
self.TableName.scrollToRowAtIndexPath(lastindex, atScrollPosition: UITableViewScrollPosition.None, animated: true)

This code help you to scroll your tableview at particular position.

Gainly answered 15/10, 2016 at 4:47 Comment(0)
T
-1
func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    tabelView.reloadData()
}

this is work for me

Turnheim answered 3/11, 2016 at 7:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.