How to prevent scrolls Up after rows are inserted on TableView - uitableview

Currently, I've a Pull Down To load more old messages on my TableView. The issue is that after new messages get append to tableview it scrolls up. What I would like to do is to maintain the same scrollPosition after new messages are loaded on the Tableview.

You could accomplish this by saving the current offset before the new messages are appended, and scrolling (back) to that offset after the messages were added.
If you're using an NSFetchedResultsController, you could handle this in controllerWillChangeContent: and controllerDidChangeContent:.
You will have to defer your scrolling, or disable animation from the insert (or scrollToRow), since the table can't smoothly animate back up to the previous offset, in the midst of animating down due to the inserted rows.

Related

When implementing infinite scroll (paginating results in UITableView when scrolling to bottom): reloadData or insertRows?

When implementing infinite scroll in a UITableView (loading additional results to the data source when the user scrolls to bottom), should the table be updated by reloadData() or by inserting rows (perhaps through a batch update)?
I'm using reloadData() right now and it works just fine but I'm not sure what's happening behind the scenes and how efficient this is. With reloadData(), the new rows are inserted at the bottom of the table and the table doesn't jump (so to the user it's a seamless update), but is UIKit reloading the entire table unnecessarily (and we just don't see it)? Because new rows are only added to the table when the user is scrolled to the bottom, this could mean that there are a large number of cells above it and out of view. I know those cells aren't in the view hierarchy anyway (because the table adds and removes cells as the user scrolls), but does reloadData() efficiently update pagination blocks regardless of this mechanism?
Since there are actually very few cells on screen at anytime, reloadData isn't really noticeable slower than insert. The real difference is in the animation. reload just adds the new cells with no animation where as insert will allow you to animate the cells in. The newer way (iOS 13+) to do this is to not call either method but instead use UITableViewdiffableDatasource and it will perform the update automatically with the animation you specify (this is much more similar to how RxCocoa and IGListKitWork).

UITableView is crashing due to reload data at the same time as scrolling the tableview

I am using UITableView to show a list of records. My table allows refresh from top and bottom. On refreshing from top, the old records remain visible until new data is fetched. After the data is fetched, all the records from the array are removed and new records are filled. Then as a final step I refresh the tableview to reflect the newly fetched data.
All works like a charm.
BUT...
Its causing a crash when we do monkey testing. During the top refresh, when the data is being fetched, if I keep scrolling, pulling the tableview, after several attempts there comes an unlucky moment when array is being removed to add the new records but at that time UITableView is also laying out the cells due to constant pulling. This causes the crash.
An easy fix would be to empty the tableview when doing a top refresh but that don't look good as far as the ui aesthetic is concerned. Also if the call fails I won't have anything to show to the user.
I tried by encapsulating the refreshing code in tableview's beginUpdate and endUpdate block but that won't run as there is no change in the tableview itself (i.e. no adding/deleting).
Any help would be appreciated.
Since you say you scroll away and crashes is probably because you try to insert data to indexes that are no longer visible. You should somehow check at what index you are before trying to update the table.
Some code would help me better understand.

Insert and delete rows in UITableView before first appearance on screen

I perform a bunch of row/section inserts and deletions with insertSections, insertRows, deleteRows etc. during the startup of a view controller. The manipulations are triggered by events of a background process (of course the actual calls to the table view are performed on the main thread; also, they are wrapped in beginUpdates and endUpdates).
When I start off these row manipulations in viewWillAppear I eventually get a crash because of an inconsistency in the row data: attempt to delete row 1 from section 0 which only contains 1 rows before the update. When this happens, there actually are 2 rows in the section before the update which apparently is not correctly recognized by the table view.
However, when I start the exactly same sequence of manipulations in viewDidAppear, there's no crash and the rows animate in and out as expected.
This looks like the table view has problems with a fast-running sequence of inserts and deletes before it appears. Is this a known limitation, or do you have another explanation for this issue?
Looks like you dont need to manipulate actual UITableViewCells, you may just manipulate some kind of view models (plain objects), prepare them, and then reload UITableView with them on viewWillAppear

iOS: How to prevent table from updating if row is currently being swiped?

I have a table that is being populated by a Firebase database. The user can swipe each row left and right, which causes the row to disappear. This is coming from this library: https://github.com/alikaragoz/MCSwipeTableViewCell
If I'm in the middle of swiping a row and the table updates with new data, my swipe gesture resets and the row that I was swiping gets pushed down.
Is there a way to prevent updates on the table while one of the rows is currently being interacted with?
Probably, the reason of the issue is that you are -somehow- calling tableView.reloadData() after getting new data (which sounds ok), the updating of the table view causes to edit the number of its rows which leads to push the current swiped cell.
The solution is to check if there is a cell swiping currently happening to prevent the calling of tableView.reloadData(), this should be done after the swiping finished. Fortunately, after checking the used MCSwipeTableViewCell library, I found how to check such a functionality, by conforming to MCSwipeTableViewCellDelegate as follows:
Implement swipeTableViewCellDidStartSwiping: to prevent tableView.reloadData().
Implement swipeTableViewCell:didEndSwipingSwipingWithState:mode: to make sure that the swiping ended and then calling tableView.reloadData().
Of course the logic of how you will do the checking is up to you, but -hopefully- this guides you to solve the issue.
Hope this helped.

UICollectionView insertItemsAtIndexPaths: seems to reload collectionview or autoscrolls to top

I am using a UICollectionView in combination with a NSFetchedResultsController. When I fetch new data, I throw them in a SQLite database and my frc gets a notification that it will update. I implement the appropriate delegate methods and insert the new data in a batch update and I don't call reloadData afterwards. But as soon as the data is inserted, the collectionview scrolls to the top (to make the new items visible?). I would like to interfere this process and imitate the Facebook/9Gag implementation. When there is new data, a button is faded in which lets the user scroll to the top. At the time the button is shown, the new items are already inserted, so the user can also just scroll up himself. I tried the following:
hooking into the UIScrollViewDelegate methods to see if I could stop the collectionView from scrolling (none of the methods is called during this update process)
show the "scroll to show more"-button when the frc willUpdate and just insert the items when the button is pressed. The problem is that the user cannot scroll up himself
Before inserting the items, I calculate the indexPath of currently visible items AFTER the insertion (adding the number of inserted items) and scroll to that index without animation in the completion block of the batch update. This only works if I delay the scrolling, but then it flickers, because it's first scrolling up a little
I experimented the same problem, by calling "insertItemAtIndexPaths" to increment data into my collectionView.
I noticed two things :
- If you add items AFTER the current visible index path, there's no automatic scroll
- If you add items BEFORE the current visible index path, there's no automatic scroll, like you said, but the contentOffset is kept at the same position, but with different contentSize, because you've just added new items.
To deal with this "side effect", I used the solution mentioned here : UICollectionView insert cells above maintaining position (like Messages.app)
As it's explained in the following post, the "CATransaction.setDisableAction(false)" is the key thing to update smoothly the contentOffset position.

Resources