Detect when UITableView has scrolled to the bottom
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Detecting when a UITableView reaches the bottom is a common requirement for infinite scrolling, lazy loading, and analytics. The practical solution is to compare the current scroll position with the total content height, while also guarding against repeated triggers when the user lingers near the bottom.
Using scrollViewDidScroll
Because UITableView is a UIScrollView, you can detect bottom reach in the scroll delegate.
The threshold lets you start loading slightly before the exact bottom, which usually feels better than waiting for the user to hit the final pixel.
Why the Load Guard Matters
Without a flag such as isLoadingMore, bottom detection fires repeatedly while the scroll view remains near the threshold. That can cause:
- duplicate network requests
- duplicate rows
- wasted work
The guard turns the bottom detection into a one-shot trigger until the current load finishes.
Alternative: willDisplay the Last Cell
For some table views, detecting when the last cell is about to appear is even simpler:
This works well for straightforward infinite scrolling, though it is less precise if your content size changes dramatically or if you need prefetching before the final row becomes visible.
Insets and Empty Tables
When you compute the bottom position, include content insets or safe-area adjustments. Using adjustedContentInset is usually better than ignoring insets entirely.
Also consider empty or very short tables. If the content is smaller than the visible height, your threshold logic may trigger immediately on first layout. Sometimes that is desired, but sometimes you want to block load-more behavior until initial data is already present.
Common Pitfalls
The most common mistake is forgetting to prevent repeated triggers. If loadMore() can fire multiple times before the previous request finishes, infinite scrolling quickly becomes unstable.
Another issue is calculating the threshold from frame.height without accounting for insets. Safe areas and content insets can make the "bottom" appear earlier or later than expected.
A third pitfall is calling reloadData() too aggressively for large tables. For append-heavy lists, inserting rows or using diffable updates can be smoother than full reloads.
Finally, do not treat "scrolled near bottom" and "finished loading more data" as the same state. Keep explicit state for loading so the table does not duplicate work.
Summary
- Use
scrollViewDidScrollto compare offset, content height, and visible height. - Add a threshold so loading can begin before the user reaches the exact bottom.
- Guard with an
isLoadingMoreflag to prevent duplicate requests. - '
willDisplayon the last cell is a valid simpler alternative in some cases.' - Account for insets, empty content, and repeated callback behavior.

