I have two view controllers, A and B.

A has a tableview.

A is wrapped in a nav controller which allows it to push to B. B has a handler which calls tableView.reloadData(). However, the cellForRowAt method is not called in A until B has popped and A is visible again.

Is there formal documentation that states "cellForRowAt" is called only when the VC is the topmost view controller? This IS the desired result, and I want to ensure this is suppose to happen.

-- denotes action
// denotes current VC
console denotes print message

--push to BVC
--press handler Button
console: "handling"
console: "nil"
console: "numberOfRowsInSection"
--press back navigation
console: "Disappearing"
console: "CellForROwAt"

Gist of files for reproducing


-- Code --

class AViewController: UIViewController {

    @objc private func pushBVC() {
        let bVC = BViewController()
        bVC.handler = {
        navigationController?.pushViewController(bVC, animated: true)


extension AViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return UITableViewCell()


class BViewController: UIViewController {

    var handler: (() -> Void) = {}

    override func viewWillDisappear(_ animated: Bool) {

    @objc private func doHandler() {


2 Answers

rmaddy On Best Solutions

When the 2nd view controller is pushed, the first view controller is actually removed from the window. So at the time you call reloadData, the table view isn't in the window hierarchy so it doesn't update itself at that time.

During the process of the 2nd view controller being dismissed and the first one being shown again, it gets added back to the window hierarchy (along with the table view), and the table view uses this event to refresh itself with the pending updates.

This is why you don't see the call to cellForRowAt until after the print("Disappearing") message.

So it's not that a table doesn't update when not visible, it doesn't update while it's not part of the window hierarchy.

GntlmnBndt On

The cellForRowAt function is called every time the UITableView needs to display a cell for a particular IndexPath, whether that be because the parent ViewController has just been loaded and the table is about to be displayed, or because the user is scrolling the tableView and new cells need to become visible.

Keep in mind that the cells themselves are reusable. When the IndexPath is no longer visible, the cell is recycled and reused for IndexPaths that are about to scroll onto the screen. When the tableView is scrolled back to a previously viewed IndexPath, cellForRowAt is called again for that IndexPath, and a newly dequeued cell of the correct type is used.

All of this should only happen when the tableView is visible. It does not need to be the topmost ViewController. On an iPad with a modal in the foreground, a tableView could still be scrollable in the background, and cellForRowAt would still be called as needed. Even on an iPhone, with an alertView on top, it could happen. As long as the tableView is displayed, and a new cell IndexPath needs to come into view, cellForRowAt will get called. For most iPhone screen situations, with standard ViewControllers, this would generally only happen on the topmost ViewController.