Moving code to willDisplayCell to improve UITableview scrolling - ios

I have seen this paragraph below from the internet and followed his recommendation and moved my code to willDisplayCell. However, I don't see any performance improvement. So I did some further investigation and found out that there are some other people saying what the paragraph said is not true. Proper Use of CellForRowAtIndexPath and WillDisplayCell . I am confused which guide I should follow at this stage as most of the time I put my code in cellForRowAtIndexPath
But very important thing is still there: tableView:cellForRowAtIndexPath: method, which should be implemented in the dataSource of UITableView, called for each cell and should work fast. So you must return reused cell instance as quickly as possible.
Don’t perform data binding at this point, because there’s no cell on screen yet. For this you can use tableView:willDisplayCell:forRowAtIndexPath: method which can be implemented in the delegate of UITableView. The method called exactly before showing cell in UITableView’s bounds.

I have done some test myself as well and found that there is no significant difference between putting the code in willDisplayCell and CellForRowAtIndexPath. So I believe the argument in the ink you have provided is more true. Unless anyone found anything different?

Related

ios/objective-c: changeUpdate and configureCell vs cellForRowAtIndexPath with NSFRC

For a tableviewcontroller I use cellforrowatindexpath to configure the cell.
However, when changing something in core data, the delegate method controllerDidChangeObjectatObjectAtIndexPath fires and for the case changeUpdate, Apple seems to require that you rewrite the row with configureCell.
Is it best practice/necessary to entirely duplicate the code for cellforRowAtIndexPath in configureCell or is there a better way to keep the code identical. For example, would there be a way to do away with one or the other code blocks.
It seems error prone and redundant to have to have the same code for configuring the cell in both cellforrowatindexpath and configure cell.
Put the common code in another method and then call this new method from cellForRowAtIndexPath and configureCell. Or simply call configureCell from cellForRowAtIndexPath if appropriate.
Do not duplicate code. Refactor common code into a method that can be called from the places you would have had the duplicate code.

How to add cell to tableview's reuse queue before 'cellForRow:'?

Creating a cell costs a lot of time and make the first scroll lagging, so I want to create a cell and add it to tableview's reuse queue before cellForRow: called.
I use dequeueReusableCellWithIdentifier: in viewDidLoad, but when I scroll the table, the cell is being created again.
In general all drawing methods of scrollview should be kept as simple as possible to avoid lag. This means you should prepare your data/model in viewDidLoad/viewWillAppear or even in previous ViewController. Your cellForRow should be as simple as set this image(s) and those text(s) - no checks, no expensive operations such as bluring, retrieving data from CoreData/Network, etc.
If you are not sure which thing exactly causes your lag, you should learn how to use TimeProfiler. If you feel lost in documentation, have a look this(quite outdated though) tutorial.
With thus said I cannot be able to help you anymore until you post some code which we could discuss.

awakeFromNib is not called for UITableViewCell object

I have a class that inherits from UITableViewCell, it has a bunch of IBOutlets. I had previously been using this object in a way that reuses the cell and initializes it as it's needed. This method is too slow, so I decided to create an array of the UITableViewCell objects and then add them as needed in the cellForRowAtIndexPath: method.
Everything gets loaded fine except the IBOutlet objects. awakeFromNib is never called so I assume this has something to do with my issue.
Just to clarify it was getting called fine when I was initializing the cells in the cellForRowAtIndexPath function, it's just when I tried to preload them in the view controllers viewWillAppear method that it breaks.
I know this is an old question. But after reading the conversation, I feel I must give some input. This a multi-layered issue that you are dealing with here.
Firstly, when you say "preload" what is it that you mean exactly.? Are you calling dequeueReusableCellWithIdentifier in your viewWillAppear? Or are you calling an init. Either way this is not an acceptable practice whatsoever.
Remember, the UITableViewCells are "Reusable" meaning that the UITableView, in effect, handles the unloading and loading of the UITableViewCells when they are offscreen to optimize performance (not memory, believe it or not) as well as some other things. Essentially, it's a pretty nifty "hack" to keep table views efficient. So if your UITableView is too slow, then you are doing something wrong, not the implementation of UITableView
When you say you are "preloading" them, that throws up some immediate red flags on your usage. And when you say "the method is too slow", well it's not. The delegate/datasource methods in place for UITableViews and UICollectionViews happen in calculated orders. Don't fight it.
Secondly, for your issue with awakeFromNib not getting called. You aren't providing enough information. How did you initialize it? If you created it in your storyboard/nib, you shouldn't be calling any of the init methods. In fact, as of iOS6 you are guaranteed a non-nil cell from dequeueReusableCellWithIdentifier.
Have you called it through its super class like :
-(void)awakeFromNib
{
[super awakeFromNib]; // Use this line
}
Hope it helps you.

Where does the indexPath of dequeueReusableCellWithIdentifier:forIndexPath: get used?

Apple's documentation says of the indexPath parameter:
The index path specifying the location of the cell. The data source receives this information when it is asked for the cell and should just pass it along. This method uses the index path to perform additional configuration based on the cell’s position in the table view.
But register(Class|Nib):forCellReuseIdentifier: only specifies the reuse identifier to use, not the section or a set of index paths.
I thought perhaps UITableViewCell had some way of getting its hands on the index path, so it could, say, round its corners if in the first row of a section, but I'm not seeing it. At creation time, all it gets is its style and reuse identifier (initWithStyle:reuseIdentifier:); at reuse time, all it gets told is prepareForReuse.
Seeing as the old dequeueReusableCellWithIdentifier: is still supported, what sort of index-path-based configuration could it possibly be doing, if it can't rely on having the chance to do it, anyway?
I checked the Table View Programming Guide, but it hasn't been updated since iOS 5.
The most important difference between dequeueReusableCellWithIdentifier: and dequeueReusableCellWithIdentifier:indexPath: is that they are different methods! Thus they can behave differently, and they do. This has nothing to do with the indexPath, really; we just need a way to distinguish them.
The New Way
In particular, if you call dequeueReusableCellWithIdentifier:indexPath:, this is a sign that you are using the new iOS 6 register-and-dequeue system. So, if you have failed to register this identifier, you'll get a nice crash and a log message explaining the problem. This method will never return nil; it always returns a cell, either by creating a new one or by reusing one.
The Old Way
On the other hand, plain and simple dequeueReusableCellWithIdentifier: is old and has to be backward compatible. If you haven't registered this identifier, it won't complain: it will just return nil, leaving you high and dry. You'll have to create the cell yourself, as in the bad old days.
EDIT: But see also the answer by #svena! The new way (with indexPath:) has a second advantage I didn't know about: the cell is correctly sized at the time it is returned to you.
According to WWDC 2012 Session 200 - What's New In Cocoa Touch,
If you use - dequeueReusableCellWithIdentifier:forIndexPath: to dequeue your cell, it will be the right size and you'll be able to do layout inside your cell's contentView.
That's pretty much a quote from Chris Parker, a UIKit Engineer.
Up until iOS 6, you had to subclass your UITableViewCell and override - layoutSubviews if you wanted to make layout adjustments. From encapsulation point of view, this might still be the better solution – however, sometimes you just need a tiny adjustment, and now you can do that in - tableView:cellForRowAtIndexPath: instead.
I believe it is used to call the tableView:heightForRowAtIndexPath: method, if one exists, allowing the cell to be correctly sized.
I always thought that UIKit would round the corners of the top and bottom cells in a grouped table view when the UITableViewDelegate method:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
is called. However, knowing the index path isn't much use unless the cell also knows how many rows are in the same section so it can work out if it's the last cell or not.
I assume that the addition of the index path to the method dequeueReusableCellWithIdentifier: is to improve performance perhaps as #jrturton suggested with different reuse pools or simply to determine the position of a cell in grouped sections.
As far as I can remember from the WWDC videos, there were a few additional methods added in iOS 6 to support reordering, insertion and deletion of cells so perhaps this also comes into factor here?

How to notice if a UITableViewCell has left the visible area?

I'm stuck with the problem that I want to know in my UITableView if a specific UITableViewCell, let's say the first, is still visible or already off the visible area.
I would also be ok to know if the cell of interest is now beeing reused at an other indexPath of the table.
One of my - later and frustrated approaches - was to have a thread that knows the first cell object and frequently pings it to check if a value I did set in the cell changed. Obviously a not so good solution.
Andy ideas how to do this right?
Remember that UITableView is UIScrollView subclass and its delegate also confirms to UIScrollViewDelegate protocol as well.
So in your table delegate you can implement scrollViewDidScroll: method and check contentOffset - if it's more then first cell height then first cell is not visible. You can also get the array off all currently visible cells using -visibleCells method, but I think knowing contentOffset should be enough.

Resources