UIScrollView
iOS Development
Scroll Detection
Mobile App Development
Swift Programming

Check if a UIScrollView reached the top or bottom

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Detecting whether a UIScrollView is at the top or bottom is mainly about comparing contentOffset with the visible bounds and insets. The only subtle part is that modern iOS layouts often include adjusted content insets, so a correct check should account for them rather than comparing against zero blindly.

Top and Bottom Are Offset Calculations

At a high level:

  • top means the vertical offset is at or above the upper inset
  • bottom means the offset plus visible height has reached the content height plus lower inset

In Swift, a reusable helper looks like this:

swift
1import UIKit
2
3func isAtTop(_ scrollView: UIScrollView) -> Bool {
4    return scrollView.contentOffset.y <= -scrollView.adjustedContentInset.top
5}
6
7func isAtBottom(_ scrollView: UIScrollView) -> Bool {
8    let visibleHeight = scrollView.bounds.height - scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom
9    let bottomOffset = scrollView.contentOffset.y + visibleHeight
10    return bottomOffset >= scrollView.contentSize.height
11}

This is a better default than checking contentOffset.y <= 0, because safe areas and automatic inset adjustments can move the true visual top.

Use the Delegate Callback

The usual place to evaluate these conditions is scrollViewDidScroll.

swift
1final class FeedViewController: UIViewController, UIScrollViewDelegate {
2    @IBOutlet private weak var scrollView: UIScrollView!
3
4    func scrollViewDidScroll(_ scrollView: UIScrollView) {
5        if isAtTop(scrollView) {
6            print("Reached top")
7        }
8
9        if isAtBottom(scrollView) {
10            print("Reached bottom")
11        }
12    }
13}

This gives you continuous updates during user interaction, which is what you need for sticky UI behavior, infinite scrolling, or pull-to-refresh logic.

Add a Threshold for Real Apps

Exact equality is often too strict. Floating-point rounding, bounce behavior, and partial visibility make threshold checks more practical.

swift
1func isNearBottom(_ scrollView: UIScrollView, threshold: CGFloat = 50) -> Bool {
2    let visibleHeight = scrollView.bounds.height - scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom
3    let bottomOffset = scrollView.contentOffset.y + visibleHeight
4    return bottomOffset >= scrollView.contentSize.height - threshold
5}

This is the common pattern for loading the next page of content before the user hits the exact last pixel.

Remember That Small Content Changes the Meaning

If the content is shorter than the visible area, the scroll view may effectively be both at the top and bottom at the same time. That is not a bug in the math; it is the correct interpretation of a view with nothing to scroll.

So if your logic triggers loading or animations, decide how to handle that case explicitly. Infinite scroll code often needs a guard to prevent repeated "bottom reached" triggers when the content is not yet tall enough to fill the screen.

Bounce and Insets Affect User Perception

With bouncing enabled, users can drag past the top or bottom temporarily. That means your check may become true before the scroll view visibly settles back. In some flows, that is fine. In others, you may want to wait until dragging ends or deceleration stops before acting.

Similarly, if the scroll view lives under a navigation bar or uses automatic content insets, the adjusted insets are part of what the user sees. Using raw bounds alone often produces checks that feel off by one navigation bar height.

Common Pitfalls

  • Checking contentOffset.y <= 0 and ignoring adjusted top insets.
  • Comparing directly against contentSize.height without subtracting the visible height of the scroll view.
  • Triggering "bottom reached" logic repeatedly when the content is shorter than the viewport.
  • Using exact pixel equality when a threshold-based check would be more stable.
  • Forgetting that bounce can make top or bottom checks fire during overscroll, not just at a settled resting position.

Summary

  • Use contentOffset, visible height, and adjusted insets together to detect top and bottom correctly.
  • 'scrollViewDidScroll is the usual place to run the checks.'
  • Thresholds are often better than exact comparisons for production UI.
  • Short content can make the view effectively both top and bottom at once.
  • Bounce and safe-area insets affect when users perceive that the scroll view reached an edge.

Course illustration
Course illustration

All Rights Reserved.