0

My live app crashes with following. I've done a lot of search and I understand now that I've to ensure that table reloads before calling scrollToRow. I've seen many workaround but I'm not sure which one will actually work. I've tried calling scrollToRow on main thread or calling after a delay but that doesn't solve my problem. I've load more (pagination) implemented which get messed up if I use fix to call scrollToRow after a delay.

How do we fix this? How to know for sure that table has completed reloading?

This solution seems to be looking good but it didn't work for me. https://stackoverflow.com/a/18812830/11957965

As crash happens only on live app, I can't reproduce it during development testing so I wanted to make sure that I've a working fix.

Crash

[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:usingPresentationValues:]: section (0) beyond bounds (0).

Code

if result!.data!.items!.count > 0 {
    self.msgTable.reloadData()

    let lastIndexPath = IndexPath(row: 0, section: 0)
    self.msgTable.scrollToRow(at: lastIndexPath, at: UITableView.ScrollPosition.top, animated: false)
}

performBatchUpdates Solution

// This one crashes
if #available(iOS 11.0, *) {
    self.mesgTable.performBatchUpdates({
        let index = IndexPath(row: 0, section: 0)
        self.mesgTable.insertRows(at: [index], with: .none)
    }, completion: { (success) in
        if self.currentPage == 1 && self.dataSource.count > 0 {
            let lastIndexPath = IndexPath(row: 0, section: 0)
            self.mesgTable.scrollToRow(at: lastIndexPath, at: UITableView.ScrollPosition.top, animated: false)
        }
    })
} else {
    if self.currentPage == 1 && self.dataSource.count > 0 {
        let lastIndexPath = IndexPath(row: 0, section: 0)
        self.mesgTable.scrollToRow(at: lastIndexPath, at: UITableView.ScrollPosition.top, animated: false)
    }
}

Crash

** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'

Oliver
  • 13
  • 1
  • 6
  • `.reloadData()` reloads the visible rows... are you getting an error because your data has changed and you have Zero rows (an empty table) when you call `.scrollToRow(...)`? – DonMag Aug 21 '19 at 16:24
  • I've a condition which check for count > 0. I've updated my code. If there are no data in the dataSource then reload and scrollToRow will not be called at all. – Oliver Aug 21 '19 at 16:28
  • Did you call `reloadData()` on main thread either? – CenoX Aug 21 '19 at 16:59
  • *`result!.data!.items!.count`* The fact you have it named **result** makes it sound like you are in a completion block from an async / background process... Are you calling `reloadData()` on the main thread? – DonMag Aug 21 '19 at 17:22
  • Yes you are right I'm on async and yes I'm calling reload on main thread as the return from async is on main thread. DispatchQueue.main.async { completed(resultData,nil) return } – Oliver Aug 21 '19 at 17:37

2 Answers2

1

Instead of reloadData, you can use performBatchUpdates with completion handler. With this method you manually insert the items and control the IndexPaths. This way you can insert with animation and scroll in the completion with animation. Look at this doc for reference:

https://developer.apple.com/documentation/uikit/uitableview/2887515-performbatchupdates?language=objc

Eyzuky
  • 1,843
  • 2
  • 22
  • 45
  • I tried a solution but it crashes. I'm not sure if I'm doing it correctly. I've update question with this solution. – Oliver Aug 21 '19 at 17:06
  • @Oliver why so you call reload data in the completion handler? If you use performBatchUpdates, you control all reload operations. Calling reload data is fundamentally wrong here – Eyzuky Aug 21 '19 at 17:11
  • I'm not sure how to use batch update. I've removed reloadDat but it still crashes with same reason. I've updated code in my question. – Oliver Aug 21 '19 at 17:42
  • I recommend learning. You can search online and see examples. It is not too complicated. In your code it seems weird to insert index 0,0, not sure what you are trying to achieve – Eyzuky Aug 22 '19 at 07:23
0

Try this approach:

Check if the table has the index path. If so, scroll. If not, dispatch for another 0.5 seconds later and try again. You can do this 2-3 times until it works.

I didn’t give the full code as I am answering from my phone, but it should be pretty easy to figure out

Eyzuky
  • 1,843
  • 2
  • 22
  • 45
  • I understand what you are saying. Dispatching after N seconds doesn't work when I want to scroll to bottom. I've load more on table which messes up when I do dispatch after N secs. I was looking for a solution which actually works. I've just tried the closure solution mentioned above in my question. It didn't 'work. – Oliver Aug 21 '19 at 16:25
  • I guess you should use performBatchUpdates with a completion handler if that is the case. Look at this doc: https://developer.apple.com/documentation/uikit/uitableview/2887515-performbatchupdates?language=objc – Eyzuky Aug 21 '19 at 16:38