I as wondering how expensive reloadData on tableView is. I wanted to refresh the tableview every 5 seconds. Is that going to cause any performance issues?
Reload data will call the two main methods on your table view data source, numberOfRowsInSection and then iterate over cellForRow:atIndexPath: for the visible cells required, depending on your tableView's contentOffset
If your app scrolls nicely already then the only performance hit your app will endure is if you're doing a lot of work in numberOfRowsInSection (like hitting the network or something time consuming).
Edit: As noted, heightForRow:atIndexPath: can also be a pain point if you're using it to do complex calculations for different height cells.
In addition to #Jessedc answer,
If all your rows are of same height, then heightForRowAtIndexPath is more expensive than tableview.rowHeight.
If you have a lot of rows with varying heights for each row, and you are calculating the height of the row each time the heightForRowAtIndexPath method is called, then its even more expensive.
There are a lot of factors involved in deciding how expensive the reloadData call is. Everything depends on what and how much you are doing each time the delegate methods are called.
If applicable to your situation, an NSFetchedResultsController can notify your table view when the data changes, instead of refreshing it every "n" seconds.
Related
My sport app keeps track of who is on and off the field, and for how long. I normally have 20-30 people to swap around, during training time. I have a timer to reload the visible cells of the collection view every second. The whole collection view reload can cost between 120-150ms.
The scrolling performance is good, however, sometimes the tapping to swap players does not respond. I think that is when the collection view is trying to reload the cells.
I can see there might a couple of ways:
Use UITableView, with each row to have 4 different elements.
Use UIScrollView, and preload all elements there. We used to use KKGridView library here, but it is way outdated. That is why we move to UICollectionView after 6-7 years
Have anyone had the same issue, and which way should provide the acceptable level of efficiency but not too complex to implement (to reduce the number of bugs)
Keep the record of the cell's index that need to update, then get the Cell from collection view, call method ask it to update itself, push the view update to the cell class, it wont mess with your collection view tap/drag...
Reload whole collectionView is only needed when whole dataSource change, yours is simply cell-by-cell update as I notice
As it's clear from the title, I have a scrolling performance issue in a table view.
Since I have read nearly every question that is posted online in this regard, and I assume all of you have much more experience with UITableView and its techniques, I won't bother with general stuff, and I just wanna point out some key things in my code that may help you help me spot where I'm doing wrong.
The UI in each cell is very very basic, so rendering each doesn't take considerable time. No shadows, no rounded corners, no extra effect, nothing. Just a few labels and two images, that's all.
The datasource is an NSArray which is already fetched from CoreData. The data of the labels are set from the content of the array, without much calculations or process required.
The height is each cell is a static integer, so the tableView:HeightForRowAtIndexPath: will immediately return the result as fast as possible. No calculations required.
The tableView:CellForRowAtIndexPath: dequeues and reuses cell with reusable identifiers so any re-creation is avoided.
So far everything is perfectly smooth. The issue is where items in Core Data are fetched from a server (Which is extremely fast) as user scrolls down. Data binding is done inside tableView:willDisplayCell:atIndexPath: to prevent tableView:CellForRowAtIndexPath: from becoming slow, as data needs to be loaded just before the cell goes live on the screen. I also fetch new items from server inside this method whenever there're some cells remaining till the last item fetched. So for example when there are totally 50 cells data fetched and put in the CoreData already and this method is called for cell number let's say 40, I request another 50 cell data from server, so that it will be ready whenever user reaches the end of the table.
As I expect this should only be called for the cells that go live on the screen. But putting some NSLogs shows that it is called multiple times until next 200 cells data are fetched (I guess the amount changes depending on device or simulator and the memory available on them and also OS limits). For example, I'm testing on an iPhone 7+, and I start the app and I go the page in which the table is. It fetches first 50 items and only first 4 items are shown on the screen, But I see that tableView:willDisplayCell:atIndexPath: is also called for cell #25, so another 50 is fetched immediately, and then it is called for cell #75, so another 50 is fetched, and this goes on for like first 200-300 cells, and then when fetching is stopped, scrolling is extremely fast and optimized until next 200-300 cells are fetched.
What can I do? Shouldn't tableView:willDisplayCell:atIndexPath: fire whenever a cell is about to be displayed? Where else should I fetch data as user scrolls?
Any ideas or suggestions is REALLY and GREATLY appreciated.
As the user types, I am retrieving results (text) by traversing a local word graph in a background thread. Each keystroke cancels the previous operation. If the operation completes, the data source is updated and reloadData is called on the main thread. This works great and is very fast (as fast as the user can type), even when tens of thousands of results are returned.
To customize the size of each collection view cell, I implemented sizeForItemAtIndexPath for the UICollectionViewDelegateFlowLayout delegate. Unfortunately, this results in a small, but noticeable lag when the user types. To be sure the time was not lost in my size calculation logic, I tried just returning a fixed size, but it still killed performance. I am surprised at this because there are only ~120 cells or so on the screen at any given time. When commenting out this method, the response time is again immediate, even for very large data sets.
Any ideas for improving the performance of UICollectionView with custom cell sizes?
Thanks
Additional clarification...
The program returns all possible words from the given set of letters then sorts by score or alphabetically, etc. As the user types, the total word count goes up fast (exponentially, if multiple wildcards are entered). The words change as you type so the width of the cells update accordingly and wrap to the next line as handled by the flow layout.
The issue seems to be the number of cells shown on the screen at any given time. In sizeForItemAtIndexPath, if I just return a large size where only one or two cells are visible, the update is very fast; however, if I return a size that just fits the text, I end up with 100+ visible cells and there is a lag. If I comment out sizeForItemAtIndexPath and just use a fixed size cell, it is fast, but that is not what I am going for.
You don't need to reload UICollectionView by calling reloadData , instead you can use:
[self.collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
Here, indexPath is the NSIndexPath object for the corresponding UICollectionViewCell object
that you want to update.
So I am using a UICollectionView with an NSFetchedResultsController with cache (leaning on Ash Furrow's implementation https://github.com/AshFurrow/UICollectionView-NSFetchedResultsController). Everything is functional, but I'm getting sluggish response due to what seems like redundant cycling of the uicollectionviewcontroller delegates.
This is what I'm doing:
Every few seconds, I am inserting a new entity into the managedObjectContext. Each entity ends up in it's own section based on the value of section key attribute when fetched. Each entity has 22 different attributes that end up being presented in 22 new cells (rows) for the entity section.
This is what is happening:
My NSFetchedResultsController delegates appear to working as expected (they capture the single section entry). When I end up running the batch updates, it calls cellForItemAtIndexPath for only the new entity (as expected). BUT (and this is MY ISSUE) the UICollectionViewController cycles through numberOfItemsInSection and sizeForItemAtIndexPath (I have different sized cells for the 22 new cells) for every single existing section. Isn't this cached? Don't we already know this? Any ideas why this is happening? I'm a noob, so am I misunderstanding something fundamental here? Am I fundamentally implementing the sections and rows of the collectionView incorrectly?
And the end result is that the extra cycles become very expensive as the number of entities/sections increase and the ui quickly becomes too staggeringly sluggish to be acceptable/usable.
Ideas?
UPDATE: REASONING FOUND
So I guess I needed to delve deeper into the implications of using custom cell formatting calls in my delegate. I don't really understand why this needs to be so costly (calling it on every row not just those in view), but it is.
heightForRowAtIndexPath being called for all rows & how many rows in a UITableView before performance issues?
So I guess I needed to delve deeper into the implications of using custom cell formatting calls in my delegate. I don't really understand why this needs to be so costly (calling it on every row not just those in view), but it is.
This SO thread talks about it:
heightForRowAtIndexPath being called for all rows & how many rows in a UITableView before performance issues?
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.