You are currently viewing How to Find All Fully Visible Cells in Table and Collection View

How to Find All Fully Visible Cells in Table and Collection View

Imagine you are given a task to find out which table view cells that are currently fully visible on screen. So you fire up Xcode, access the UITableView‘s indexPathsForVisibleRows and expect that to get the job done.

Very soon, you will notice that something is not right. The result you get is a bit off. Cells that are partially visible on screen are also included in the result, and this is not what you want. How should you go about this?

The solution is actually pretty simple, all you need to do is think out of the box. Let me show you how.


Getting the Index Paths for UITableView

In order to find index paths for all fully visible cells, we must first have a way to determine if a cell is completely visible based on its index path.

So what is another way of saying “a cell is fully visible”?

A cell where its content view completely intersects with the table view is considered a fully visible cell.

With that in mind, we translate the above statement into code like so:

let cellFrame = tableView.rectForRow(at: indexPath)
let isCellFullyVisible = tableView.bounds.contains(cellFrame)

Once we are able to determine if a cell is completely visible based on its index path, getting index paths for all fully visible cells has become fairly straightforward.

let visibleIndexPaths = tableView.indexPathsForVisibleRows!
let fullyVisibleIndexPaths = visibleIndexPaths.filter { indexPath in
    // Filter out all the partially visible cells   
    let cellFrame = tableView.rectForRow(at: indexPath)
    let isCellFullyVisible = tableView.bounds.contains(cellFrame)
    return isCellFullyVisible
}

That’s it for table view. Next up, collection view!


Getting the Index Paths for UICollectionView

In terms of the collection view, the general approach is basically the same. However, the collection view works a bit differently from the table view, we can’t get the collection view cells’ frame directly. We must access the cell’s frame via its layout attribute like so:

let layoutAttribute = collectionView.layoutAttributesForItem(at: indexPath)!
let cellFrame = layoutAttribute.frame
let isCellFullyVisible = collectionView.bounds.contains(cellFrame)

The filtering part is basically the same as the table view, just make sure to use indexPathsForVisibleItems instead of  indexPathsForVisibleRows.

let visibleIndexPaths = collectionView.indexPathsForVisibleItems
let fullyVisibleIndexPaths = visibleIndexPaths.filter { indexPath in
    // Filter out all the partially visible cells
    let layoutAttribute = collectionView.layoutAttributesForItem(at: indexPath)!
    let cellFrame = layoutAttribute.frame
    let isCellFullyVisible = collectionView.bounds.contains(cellFrame)
    return isCellFullyVisible
}

Taking One Step Further

As a good developer, we should always strive for writing clean and reusable code. Thus, let’s make the code I just show you an extension of the table view and collection view functionality.

extension UITableView {

    func isCellAtIndexPathFullyVisible(_ indexPath: IndexPath) -> Bool {

        let cellFrame = rectForRow(at: indexPath)
        return bounds.contains(cellFrame)
    }

    func indexPathsForFullyVisibleRows() -> [IndexPath] {

        let visibleIndexPaths = indexPathsForVisibleRows ?? []

        return visibleIndexPaths.filter { indexPath in
            return isCellAtIndexPathFullyVisible(indexPath)
        }
    }
}
extension UICollectionView {

    func isCellAtIndexPathFullyVisible(_ indexPath: IndexPath) -> Bool {

        guard let layoutAttribute = layoutAttributesForItem(at: indexPath) else {
            return false
        }

        let cellFrame = layoutAttribute.frame
        return self.bounds.contains(cellFrame)
    }

    func indexPathsForFullyVisibleItems() -> [IndexPath] {

        let visibleIndexPaths = indexPathsForVisibleItems

        return visibleIndexPaths.filter { indexPath in
            return isCellAtIndexPathFullyVisible(indexPath)
        }
    }
}

With that in place, getting index paths for all fully visible cells has become as simple as:

let fullyVisibleIndexPaths = tableView.indexPathsForFullyVisibleRows()
let fullyVisibleIndexPaths = collectionView.indexPathsForFullyVisibleItems()

Pretty clean isn’t it?


If you enjoy reading this article, feel free to check out my other iOS development related articles. You can also follow me on Twitter, and subscribe to my monthly newsletter so that you won’t miss out on any of my upcoming iOS development-related articles.

Thanks for reading. 👨🏻‍💻


👋🏻 Hey!

While you’re still here, why not check out some of my favorite Mac tools on Setapp? They will definitely help improve your day-to-day productivity. Additionally, doing so will also help support my work.

  • Bartender: Superpower your menu bar and take full control over your menu bar items.
  • CleanShot X: The best screen capture app I’ve ever used.
  • PixelSnap: Measure on-screen elements with ease and precision.
  • iStat Menus: Track CPU, GPU, sensors, and more, all in one convenient tool.