Insert and delete rows in UITableView before first appearance on screen - ios

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

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).

Do I have to call begin/endUpdates method when a new row is inserted into the tableView?

Description:
A table view with 3 rows is created when the app is started. A add button in the navigation bar will add a new row into the table view if tapped. The core process behind the add row action is the method func insertRows(at:, with:).
Apple documents and many stack-overflow answers (e.g., this one) suggest that put any method calls that insert, delete, or select rows and sections of the table view between function beginUpdates() and endUpates().
I removed the beginUpdates() and endUpdates(); it turns out the app works just fine without them.
Question:
What are the benefits of adding beginUpdates() & endUpdates()?
Why I ask this question:
The function func insertRows(at:, with:) is calling the delegate function tableView(_:, cellForRowAt:) -> UITableViewCell to create a new cell via dequeueReusableCell(withIdentifier:, for:) -> UITableViewCell, this is the same procedural the tableView creates the 3 default rows when the app started, but, there is no such a beginUpdates / endUpdates there. So why do I need to add this procedure later when I add a new row, while without them the app still seems to be working?
To animate a batch insertion, deletion, and reloading of rows and sections, call the corresponding methods within an animation block defined by successive calls to beginUpdates and endUpdates. If you don’t call the insertion, deletion, and reloading methods within this block, row and section indexes may be invalid. Calls to beginUpdates and endUpdates can be nested; all indexes are treated as if there were only the outer update block.
At the conclusion of a block—that is, after endUpdates returns—the table view queries its data source and delegate as usual for row and section data. Thus the collection objects backing the table view should be updated to reflect the new or removed rows or sections.
Apple Docs

Refresh the UITableView with a complete refresh of the data

I have a view which shows a list of entries in a UITableView. The data for the list is an NSArray. When the user is on this view, there is an asynchronous call to a webservice to retrieve the latest entries. When I have downloaded the latest entries, I update the NSArray and then I send off a notification to the notification center. The UITableView is listening to this event and will try to reload the table.
But with this approach I get an error which is on the lines of "The object was mutated during reading".
After going through similar questions here on SO, I see the suggested approach is to remove objects and add objects at individual indexes and ask the table view to reload the data.
Is there a way to replace the array completely and ask the table view to reload the data ?
UITableView's reloadData method should do the trick.
From Apple's documentation:
Call this method to reload all the data that is used to construct the
table, including cells, section headers and footers, index arrays, and
so on. For efficiency, the table view redisplays only those rows that
are visible. It adjusts offsets if the table shrinks as a result of
the reload. The table view's delegate or data source calls this
method when it wants the table view to completely reload its data. It
should not be called in the methods that insert or delete rows,
especially within an animation block implemented with calls to
beginUpdates and endUpdates
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITableView_Class/index.html#//apple_ref/occ/instm/UITableView/reloadData
if you have Updated NSArray Object
Then you can use directly following method of UITableView
[tableView reloadData]

tableView still shows two rows even though I deleted one from the data source and reloaded the table

I have an NSMutableArray _conversationsArray that basically stores IDs of all the conversations a user has in my app. Based on that value I show different number of rows in the tableView. When I delete a conversation from the data source and trigger reload of the tableView, numberOfRowsInSection method triggers and by setting a breakpoint I check the number of elements (conversations) in that array:
Everything seems to be fine, the result is one object which is great because I had two of them before the deletion. The problem is, as you can see on the far left of the screen, both rows are still visible in the tableView (even after the breakpoint) :/ The second row should have disappeared but it didn't. It did become unresponsive to touch events, but I need it gone.
Have you ever experienced a problem like this? Any help would be greatly appreciated.
--- Update ---
Could it be that it has to do something with the threads? because it seems that the table updates as it should when the thread changes :/ I might be wrong about it as it's hard to debug
It looks like you're not reloading the table view on the main thread, since -tableView:numberOfRowsInSection is called on a background thread (?). In UIKit -reloadData must be called on the main thread.

iOS iterate UITableView

I have a UITableView that collects data from a database. What I would like to know is if there is some way I can iterate in the UITableView collection and check the values of the cell? The reason I ask is because I would like to update each cell based on the current value that it has (change font, size, color, etc.). I've seen in another SO post regarding this topic, but since the cells are already created and their values are changed it is a bit harder for me. I was thinking of iterating through the UITableView before I call reloadData, but any other suggestions are welcome.
You should not iterate over the cells of UITableView, because some of them (in fact, most of them) may not be present until you request them. UITableView aggressively recycles its cells, so if a cell is not visible, it is very likely that you would be creating it from scratch only to put it back into recycle queue moments later.
Changing your model and calling reloadData the way your post suggests would be the right solution. iOS will ensure that it runs the update in a smallest number of CPU cycles possible, so you do not need to worry about the cells that are already created. This is also the easiest approach in terms of your coding effort.
A table view is for displaying data. The properties of your table cells should only be written to, not read from. The appropriate way of handling this situation would be to update your underlying model objects -- the objects that you use to populate the table view -- as the data changes, and then reload the affected rows.
The issue you'll encounter is that UITableView reuses table cells. Once a table cell scrolls off the screen, it's quite likely that the table view will reuse the same cell to display a different row.
This means it's fundamentally not possible to iterate over the table cells. When you need to refresh a row because its data has changed, you should call reloadRowsAtIndexPaths:withRowAnimation: (or reloadData if all rows have changed) and if the row is visible on screen, UITableView will call your data source methods and give you an opportunity to configure the cell for display.

Resources