Just as the question\'s title mentions:
What\'s the difference between \"cellForRowAtIndexPath
\" and \"willDisplayCell
: forRowAtIndexPath
I faced an issue with cell configuration in willDisplayCell:
when used autolayout and UITableViewAutomaticDimension
. The heights of reused cells didn't calculate properly. Printing methods in NSLog shows that willDisplayCell:
is called after heightForRowAtIndexPath:
(tested on iOS 10.2 simulator)
cellForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0}
heightForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0}
heightForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0}
willDisplayCell: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0}
cellForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1}
heightForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1}
heightForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1}
willDisplayCell: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1}
I think this was the reason, because problem has gone after placing configuration code in cellForRowAtIndexPath:
P.S. There is an opposite article to the link and quote posted by @iTSangar: https://tech.zalando.com/blog/proper-use-of-cellforrowatindexpath-and-willdisplaycell/
The delegate method willDisplayCell
will be called every time a cell is just about to get displayed on the screen. Say for example - you are downloading any image data, then this is the best place to do the download or call the method that downloads image data.
The problem with downloading image data in cellForRow
or cellForItem
is, a large number of images/photos comes back from the initial request. The user may never even scroll down far enough to see all of the images. This is very costly as it may eat up users cellular data.
So the big vote here for willDisplayCell
is User may never scroll down, so why download all images in cellForRow
?. Make use of willDisplayCell to only download those images user is about to see.
So this way you supply the cell in cellForRowAtIndexPath
and update the content in willDisplayCell
Also note that cellForRow is called first and then willDisplayCell.
cellForRowAtIndexPath
should actually return a cell instance. It should be a re-used cell, when possible. This method is required for the UITableViewDataSource Protocol. This is normally where you select the data that will be displayed in the cell. With dynamic cells, it's common to set UI properties such as selected state at the same time you set the data here.
willDisplayCell
is optional and is called after. This is your last chance to customize the cell before it's displayed. At this point, the cell instance has already been created. You can change things like selected state, etc. here. You should not be changing the data/structure of the cell or instantiating anything new, but only changing the state of UI properties for the cell. This is commonly used with static cells.
Despite what might seem intuitive, willDisplay cell: is called immediately after cellForRowAt indexPath: is called.
I had an app where images and video would be loaded in from either a URLCache or downloaded and displayed in the cell. I noticed whenever I'd start my application all the videos and images would load before I could see them and I noticed this because I could hear the audio from the last video in the tableView while looking at the first item in the tableView. This is with 8 items in the tableView. I printed to the console to get a better idea of what the order of the delegate functions calls were.
The results:
The Code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "feedCell", for: indexPath) as! FeedCell
print("Created cell at row \(indexPath.row)")
let post = posts[indexPath.row]
cell.post = post
cell.viewController = self
cell.configureCell()
return cell
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let feedCell = cell as? FeedCell{
print("Drew feed cell at row \(indexPath.row)")
// only want download task to start when the cell will display
if feedCell.post.isVideo {
feedCell.loadVideo()
} else {
feedCell.loadImage()
}
}
}
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
print("Stopped showing cell at row \(indexPath.row)")
if let feedCell = cell as? FeedCell{
feedCell.player?.pause()
}
}
You are right, cell configuration can (in theory) be done in both methods.
However, almost all UITableView
have a data source which implements cellForRowAtIndexPath:
(it is a required method in the protocol). On the other hand, the willDisplayCell:forRowAtIndexPath:
(which is a method of the delegate, not the data source) is optional.
As configuring a cell is usually dependent on the data you want to show, cellForRowAtIndexPath:
is by far the most common place to do cell configuration. (I can't even remember using willDisplayCell:forRowAtIndexPath:
).
There's one notable exception: when you are using a storyboard and static cells (instead of cell prototypes), you can't do anything useful in cellForRowAtIndexPath:
(because dequeueReusableCellWithIdentifier:
returns nil
), so you have to do configuration in willDisplayCell:forRowAtIndexPath:
, viewWillAppear:
or other methods.
@NSDeveloper: you're right. Thanks for the hint.
I think this answers your question:
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.
From Perfect smooth scrolling in UITableViews