iOS UITableView: what's the different between “cellForRowAtIndexPath” and “willDisplayCell: forRowAtIndexPath:”

前端 未结 6 961
别那么骄傲
别那么骄傲 2020-11-30 01:06

Just as the question\'s title mentions: What\'s the difference between \"cellForRowAtIndexPath\" and \"willDisplayCell: forRowAtIndexPath

相关标签:
6条回答
  • 2020-11-30 01:37

    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/

    0 讨论(0)
  • 2020-11-30 01:38

    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.

    0 讨论(0)
  • 2020-11-30 01:39

    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.

    0 讨论(0)
  • 2020-11-30 01:39

    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:

    • Created cell at row 0
      • Drew feed cell at row 0
    • Created cell at row 1
      • Drew feed cell at row 1
    • Created cell at row 2
      • Drew feed cell at row 2
    • Created cell at row 3
      • Drew feed cell at row 3
    • Created cell at row 4
      • Drew feed cell at row 4
    • Created cell at row 5
      • Drew feed cell at row 5
    • Created cell at row 6
      • Drew feed cell at row 6
    • Created cell at row 7
      • Drew feed cell at row 7
    • Stopped showing cell at row 2
    • Stopped showing cell at row 3
    • Stopped showing cell at row 4
    • Stopped showing cell at row 5
    • Stopped showing cell at row 6
    • Stopped showing cell at row 7

    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()
        }
    }
    
    0 讨论(0)
  • 2020-11-30 01:41

    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.

    0 讨论(0)
  • 2020-11-30 01:56

    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

    0 讨论(0)
提交回复
热议问题