🔋

How to Disable Scrolling in SwiftUI When Content Fits

(Updated on )

TL;DR: In iOS 16.4+ (and iOS 26), the optimal solution to fix “non-scrolling content” behavior is the .scrollBounceBehavior(.basedOnSize) modifier. For more complex scenarios requiring layout switching, ViewThatFits is the recommended approach.

By default, a SwiftUI ScrollView always allows the user to drag and bounce the content, even if the content height is smaller than the screen height. In many UI designs, this “always-on” bounce can feel unpolished. This article explores three solutions ranging from native APIs to layout tricks.

Solution 1: The Native Gold Standard (iOS 16.4+)

This is currently the standard practice. Using the scrollBounceBehavior modifier, the system automatically detects the relationship between the content size and the container size.

  • Use Case: Most standard lists and scroll views.
  • Pros: One line of code, system-level support, optimal performance.
Swift
struct BasedOnSizeView: View {
    var body: some View {
        ScrollView {
            VStack {
                ForEach(0..<5) { idx in
                    Text("Item \(idx)")
                        .frame(maxWidth: .infinity)
                        .padding()
                }
            }
        }
        // Key code: Allow scroll bounce only when content exceeds container size
        .scrollBounceBehavior(.basedOnSize, axes: .vertical)
    }
}

Solution 2: Layout Switching (ViewThatFits)

If you want to completely change the view structure when content is sparse (e.g., centering the content using a VStack when it fits, but using a ScrollView when it overflows), ViewThatFits is the best choice.

  • Use Case: Scenarios requiring completely different layouts for scrolling vs. non-scrolling states.
  • Mechanism: It attempts to render the views provided in the closure in order, selecting the first one that “fits” within the available space.
Swift
struct AdaptiveScrollView: View {
    let contentString: String
    
    var body: some View {
        // 1. Try presenting content directly first (no scrolling)
        // 2. Fallback to ScrollView if height overflows
        ViewThatFits(in: .vertical) {
            textContent
            
            ScrollView {
                textContent
            }
        }
        .frame(height: 200)
        .border(.gray)
    }
    
    var textContent: some View {
        Text(contentString)
            .padding()
            .fixedSize(horizontal: false, vertical: true) // Ensure text expands vertically
    }
}

Solution 3: Low-Level Control (Introspect)

In rare scenarios where you need granular control via underlying UIScrollView properties (such as completely disabling gesture interaction via isScrollEnabled = false rather than just disabling the bounce), you can use the Introspect library.

Note: In the era of Swift 6 and iOS 26, the first two native solutions cover 99% of requirements. This should be considered a last resort.

Swift
import SwiftUIIntrospect

ScrollView {
    // Content...
}
.introspect(.scrollView, on: .iOS(.v16, .v17, .v18, .v26)) { scrollView in
    // Manual logic to check content size vs bounds
    if scrollView.contentSize.height <= scrollView.bounds.height {
        scrollView.isScrollEnabled = false
    } else {
        scrollView.isScrollEnabled = true
    }
}

Summary

ApproachAPI RequirementComplexityRecommendation
scrollBounceBehavioriOS 16.4+⭐⭐⭐⭐⭐
ViewThatFitsiOS 16.0+⭐⭐⭐⭐⭐⭐
Introspect3rd Party Lib⭐⭐⭐⭐⭐

Further Reading

Related Tips

Subscribe to Fatbobman

Weekly Swift & SwiftUI highlights. Join developers.

Subscribe Now