Could anyone tell me the tradeoff in performance/memory usage between using static and dynamic cells in a UITableView?
Here's my situation: I have a TableView with 6 different sections. The first section is the only section in my tableView that holds a different number of cells each time the view loads, depending on the current state of the app. i.e. I have declared 12 static cells for that section in interface builder, however I only display a certain number of those cells depending on the user's interaction with the app thus far. The other 5 tableView sections all contain UISwitches and textFields that never change.
So say I statically allocated 50 cells for that first section, but still only displayed maybe just half of them depending on the state of the app. I would want to be able to display up to 50 cells though. How would this affect the speed or performance of my app? Would doing the entire tableView dynamically and redrawing the switches and textFields for the other sections each time lead to a better application performance?
UITableView itself is only tangentially related to performance in this situation. The real issue is how and when you allocate new cells.
If you have static cells whose contents never change, and you create them using the interface builder (née Interface Builder), you will see that allocation happening only once, usually in -viewDidLoad, and for the lifetime of that table (or at least until -viewDidUnload) these cells will exist and not need to be reallocated.
But this is a trade-off. Now your cells will load faster, but your app will have more memory. You'll just have to decide on a case-by-case basis whether this is slowing down your app dramatically, in which case you may want to lazily load your static cells the typical "dynamic" style in your data source cell-fetching method.
Related
I know that cellForRowAtIndexPath only loads visible cells. Is there a way to force it so that it loads the 3 cells below and above it?
No, cellForRowAtIndexPath only loads the current cell.
Your question suggests that you're doing something that is computationally expensive or slow in your cellForRowAtIndexPath. For example, you might be doing lazy loading of images, but also want to "prefetch" some of images you need for candidate "next" cells in order to diminish the user's experience of the lazy loading. Generally, though, you wouldn't actually prefetch the UITableViewCell objects, themselves, but rather just the pieces of data that those cells need.
You might need to give us more information about what sort of stuff you feel the need to prefetch, and we can provide better counsel. It's a non-trivial issue, somewhat contingent upon having a well-designed model that your controller uses when presenting the view. You might want to share a little about your model and the nature of the stuff that you want to make sure is on-hand for the previous three and next three cells.
UITableViews usually take care themselves of the whole process of deciding when to load specific cells. They automatically remove invisible cells from it, sometimes storing those cells in an internal reusability queue.
UITableView provides a mechanism you can use to speed up the cell creation process by retrieving pre-alloc'd cells when available. If you use this method properly you should have no trouble with the scrolling speed in your table views. To do so, you need to configure the reusabilityIdentifier for a cell on creation, and call the method -[UITableView dequeueReusableCellWithIdentifier:] when you need a new cell in your cellForRowAtIndexPath: implementation.
It should be feasible for you to have your own queue of reusable and preconfigured cells if you still need more speed - although the advantages you can get from implementing something like this remain to be seen (you'd have more rows ready to use, but would also slow down cellForRowAtIndexPath: for the cell being requested). You would also need to be very careful not to clash with UITableView's standard queue.
Check out Apple's UITableView reference for more info on the reusability mechanism.
I have a large number of custom views (all the same class) in a scroll view and I'm frequently getting memory warnings. Is it possible to somehow only allow a view to draw when it's about to be displayed and then release that view when it's no longer being displayed (i.e. it's been scrolled past) to reduce memory usage?
If you want to reduce the memory usage of views within a scroll view a better approach consist to define a set of views and reuse them during the scrolling process by putting them in a queue. This requires some work. I don't know how you want to layout your views but if it is a in a grid you can find some third parties libraries that does just that. One of the most famous one is called GMGridView it worth taking a look to understand how the views are being queued.
In the end I managed to solve this issue by removing all the unneeded sub views when one sub view was selected to be viewed in full and then adding them again afterwards because this isn't a hugely costly process.
In my iPad application, I've made a segmented control and one segment of which -when clicked- displays a long list (about 300) with images and labels from the local SQLite database. This is taking a lot of time to load and puts the app activity to halt while it's loading all of it from the database.
Although I've applied an activity indicator for the time being, but that looks very shoddy. Can anyone tell me how to apply Lazy Loading in a way that When the button is clicked to open that view, instead of loading all the content at once, it fetches only the content that's displayed on the content initially (about 9 images with lablels).
Thanks in advance.
You should implement paging on the list. Load first 25 item and then add button ("Next 25") on tableFooterView, which will load another 25.
If you use a UITableView, you might have a better chance.
A UITableViewCell loads cells one at a time(gives you the index of the item it is trying to load), so if you use a table view, it will only load the number of items that it needs to display. You tell it how many items there are, how tall they are, etc.
It also reuses cells, so it gives a lot better performance than creating 300 different views in memory at a time.
A UIScrollView doesn't know about your "items", so it lets you push as many items as you want into a view, and then adds a scroll bar. No optimization here for memory usage, or database access.
can someone please clarify this: when a table cell scrolls off the screen, does it still reside in memory? The reason why I am asking this is, I had to use a tableview that has to handle too many cells.
when you scroll the table up, it loads next set of visible cells. At this point, i want to make sure , that all the cells thats been scrolled off, wont be added as a heap slowing the performance. Thanks,
If you queue the cells then each cell, as soon as it disappears from the screen (that is, it is scrolled up or down), is re-used for other new cells entering in the screen.
This means that if your screen can show no more than 7 cells at the moment, the memory taken for cell allocations will not be higher than the one needed for exactly 7 of them.
The advantage of this approach is memory saving but also performance improvement as you don't need to alloc/init the cells each time.
Of course this is valid if you appropriately autorelease cells when created, if you use the same queue identifier and of course it is independent on your way to manage the data you will insert in the queue (images, strings, ...)
I'm curious just how expensive in as far as resources go is UITableView's reloadData? I have an app which will make roughly 10 subsequent HTTP requests, and as it gets data / preps, it reloads the tableView. As the data set grows larger and larger, it's becoming very sluggish. I'm trying to figure out if it's because of the amount of times I'm reloading the tableView or because of how I'm grabbing/parsing the data.
What's the best practice in this case?
From UITableView.h:
- (void)reloadData; // reloads everything from scratch. redisplays visible rows. because we only keep info about visible rows, this is cheap. will adjust offset if table shrinks
"This is cheap."
implement your table view methods well and it'll be no big deal to call this function all the time.
On a side note, you should try to use the appropriate methods to animate adding and removing rows if you are thinking of using reloadData for that.
The best practice is to have your implementation of cellForRowAtIndexPath: do as little work as possible. In fact, it really shouldn't be doing any work except populating the UITableViewCell instance with the data it needs to display.
You should be using cached UITableViewCells so you don't have to allocate a new cell each time. If you can do your parsing and such in a separate thread and make the parsed data, ready to present, accessible to cellForRowAtIndexPath:, you shouldn't have any performance problems.
You didn't say if you were using a custom UITableViewCell subclass, but if you are, deep view hierarchies can also present a performance problem, since each view in the hierarchy gets drawn. The flatter you can make UITableViewCells, the better.
Hope that gets you moving in the right direction.
Best thing to do is profile your app to see where it is slow.
That said, if your table cells are all the same height, then I think
reloadData
only has to call
cellForRowAtIndexPath
for cells that are visible on screen.
Table view reload expense is:
Figuring out how many sections and
rows per sections you have
getting row heights.
Row heights in particular are figured out for all elements of the table, anytime you call reload data.
The remaining expense is cellForRowAtIndexPath, which is usually not too bad because it only is called for as many rows as are on the screen. It can be bad when scrolling if you do not reuse cells like you are supposed to.
The key for you is probably, to ask yourself what triggers the HTML load and possibly move that into a background thread.
Boot To The Head is correct.
I'm doing a progressive one-by-one article list update in Instapaper, and I call -reloadData on each completed download. Sounds similar to what you're doing. It doesn't result in any noticeable performance slowdowns.