I need implement some kind of lazy loading in my UITableView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//... some initializations here
//this is a function that pulls my cell data using helper class
NSData *cellData=[MyDataClass dataForCellAtIndexPath:indexpath.row];
//... here I populate pulled data to cell
return cell;
}
Everything works great, but table view scrolls not smoothly, because dataForCellAtIndexPath method is slow. So I need to implement lazy populating data into cells by calling this method. The result I expect is that table view will scroll smoothly but cell's content will populate a bit after the cell is drawn. Help me please, How can it be done?
Yes, you could look at pushing your data collection onto a background thread. The first thing to try is the simplest option to see if it improves anything:
[MyDataClass performSelectorInBackground:#selector(dataForCellAtIndexPath:) withObject:indexpath.row];
This post mentions some of the other, more complex, customisable options like Grand Central Dispatch and NSThread.
If you are using a large amount of data, I would suggest saving them onto Core-data and/or use NSFetchedResultsController to populate your tableView.
It has been created & tied up with your tableView mainly to populate your tableView faster and have high performance.
Here is the link to the documentation of NSFetchedResultsController !
for that u can load your new data on the basis of index value of table cells
you can put condition for how much new data you want to load because its depends on which cell s are currently examine by the users.
Related
I have a slightly complicated table issue, I set up the table, and give it the number of sections, and number of rows per section. I don't actually have any data for the cells, they are just empty at this point.
What I want to do is when the user scrolls the cells, make an API call and get the data for that cell plus let's say the next 50 cells.
The API call is asynchronous, and once I make that call I do not want to make it for the next cell as the user scrolls, because I know that data is coming back shortly.
Any ideas or suggestions?
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableView *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Pull data for next 50 table view cells
// The data will come from a server
return cell;
}
This sounds like a great opportunity to introduce a separate service for managing your network requests instead of embedding that behavior in the middle of your table's datasource. If your datasource were to request that this service try to load the next 50 cells then that service can decide if it should actually start a new network request or not. Let your instance of that service keep track of what requests are in flight and what ranges of index paths they are expected to cover so it can decide when a -shouldLoadItemsStartingAtIndexPath: style message should result in a new network request.
Is that the sort of pattern you are looking for or did you have a more specific question about your approach?
In a UIViewController I hava a UITableView and an NSArray datalist, and I am trying to fill the tableview with datalist in [cellForRowAtIndexPath:indexPath]. In the UIViewController's viewDidLoad, I write
datalist = [LoadingService getDatalist]
The LoadingService is loading data from network to fill the datalist and the getDatalist message return the datalist. I know in viewdidload, the datalist may has no data, so no data shown in UITableView.
My question is that how can I show datalist'data in UITableView while the datalist updates?
Answer: Don't.
If you have no data, then show a spinner (UIActivityIndicatorView), or a progress view (UIProgressView) to at least indicate that your app is processing, else the user will become frustrated at the perceived lag. Then, when your function has returned acceptable data, call -reloadData and it will show your newly updated array in the table, so long as you are the delegate of the aforementioned table. A lot of servers also return responses all at once instead of chunking.
If you do happen to be working with multiple responses for one input, as each one is loaded into the array/datasource, call -reloadData to give the illusion of a "realtime update".
I have a UITableView which reloads every 3 seconds. I reload it only if my Boolean variable is true. In some conditions that variable is always true and that time table is difficult to scroll. (performance is not very good). Other times its ok. What should I do?
Note: I have coded my table according to apple's recommended way(UITableView best practises. Except I add subviews to UITableViewCell, instead drawing on it. I'm confident with other techniques).
What's the solution for this?
Are you sure that because you refresh a lot that makes you difficult to scroll. What kinds of refreshing you mean here, refreshing data from network or refresh the table view your cell.
Everytime you refresh your table view or when you scroll the table view, the table view will keep asking for the corresponding cell, and now the whole performance depends on how fast you return the cell.
If you are doing custom UITableViewCell, you are in risk of having a slow performance. Double check these things:
Do you reuse your cell correctly? Check if [tableView dequeueReusableCellWithIdentifier:] always return nil or not. If it always return nil, then you do it wrongly.
Check if you block the main thread somewhere by loading images from network or file, if it is, using multithread.
After you check everything and still has a slow performance, then you either need to reduce the times a cell is returned (by less refreshing) or draw the cell yourself
If you are fetching large data set for UITableView, try to fetch the data on need basis. Get the data for each cell in cellForRowAtIndexPath instead of getting all the data for table view and storing.
I have a grouped UITableView in my UIViewController class and I'm finding that at times, the dequeued cell is not nil as expected.
The table has 3 sections to start with and and as soon as the 'viewDidLoad' is invoked, a server call is initiated to find out if there are more sections. Before the view is even rendered, the server's response arrives and we're told that we have 4 sections. To deal with this change, I do:
[self.tableview beginUpdates]
// Do the updating of the array that holds table data
[self.tableview endUpdates]
[self.tableview reloadData];
Next I get the call to 'numberOfSectionsInTableView', for which I return 4, followed by 'numberOfRowsInSection' and I'm returning the expected correct row count. Note this is the first time I'm getting either of these calls since the table reload happened so quickly and before the view was even rendered.
At this point, only the data from the 1st 3 sections are visible and scroll to view the last section. When you scroll to see the last section, one of the cells I'm expecting to be in the call to 'tableView:cellForRowAtIndexPath' is not nil as expected. The cell type is actually what was used for another section (I have one type of UITableViewCell for the 1st two sections and another for the last two sections that I'm creating and handing back in cellForRowAtIndexPath when cell is nil).
So how does UITableView figure out which cell to dequeue and how do I figure out why this cell for this particular section/row is not nil when it really should be?
What are you using for your cell identifier? That's what a UITableView will use when determining what to dequeue. If you have different types of cells, you’ll need different reuse identifiers.
The idea of cell reuse boils down to minimizing view allocations and internal UITableViewCell setup in order to improve scrolling performance. Roughly speaking, whenever a cell goes off screen, a table view removes it from itself as a subview and adds it to a pool. The next time it needs to display a cell, you can dequeue an unused cell from the pool, configure it and return it to the table view. That's a clever implementation, but a table view cannot efficiently reuse cells without your hints, so called reuse identifiers.
The most common approach is to tie reuse identifiers to cell classes. If, say, you use cells of class A to display people from an address book and cells of class B to display organizations, you obviously cannot reuse cells B for people, and vice versa. In such a case you designate these two classes different reuse identifiers, which guarantees that when you need to provide cell A, the table will either dequeue a cell A or return nil if its pool of unused cells A is empty.
A natural extension to that is nil reuse identifiers. They tell a table view that it should not reuse them at all, releasing them when no longer on screen. Why may you need it? For unique cells or for the cells which you manage yourself, or for the cells which have an untypical life cycle, or are extremely expensive to draw. However, the fewer unreusable cells you have, the lower memory footprint.
For the app I'm currently working on, one of my main ViewControllers contains two UITableViews whose contents vary based on what's stored in my model (Core Data backed by SQL). Both tableviews set their delegate and datasource properties to the shared VC they're both subviews of, and I provide the necessary methods in the VC to respond to tableview-specific events.
I'm finding that when I add new data to my database that should cause the TableViews to show new rows, and I return to the VC and call reloadData on both TableViews in the VC's viewWillAppear method, I see that numberOfRowsInSection returns the correct number of rows based on the new entries in CoreData, but cellForRowAtIndexPath is not called the correct number of times, almost like it's not responding to the change in the model data. For example, if I had 3 rows to show then the app started, and I added another entry in CoreData, then called reloadData, numberOfRowsInSection returns 4 correctly, but cellForRowAtIndexPath is only called 3 times still.
Besides the fact that I probably be using something more suitable like NSFetchedResultsController to link my TableViews to CoreData, is there any reason why cellForRowAtIndexPath is not being called the correct number of times, as dictated by the correct number of rows returned by numberOfRowsInSection?
I would put in some of my code, but it's really standard boilerplate TableView stuff and I feel like there's something else that I'm missing.
Just to make sure you understand correctly, the cellForRowAtIndexPath: is called with the number of cell that can be in the screen. If you have 4 cells, but because the height of the cell, only 3 cells can be available to users, then the cellForRowAtIndexPath: is called 3 times
If the user can only see only 3 cells, not more, then the cell loading callbacks may only be called 3 times, even if there are 100's of rows in your table.