🔍 Disable Scroll in SwiftUI ScrollView for Small Content

Get weekly handpicked updates on Swift and SwiftUI!

TL;DR: In SwiftUI, avoid unnecessary scrolling in ScrollView when content is smaller than the container. Use scrollBounceBehavior(.basedOnSize) for iOS 16.4+, ViewThatFits for iOS 16+, and Introspect for older iOS versions to dynamically control scrolling behavior.

Problem Description

By default, ScrollView allows scrolling regardless of whether the content size is smaller than the container. This can lead to unnecessary interactions and detract from the user experience.

Solutions

Here are methods to address this issue across different iOS versions:

1. Using scrollBounceBehavior (iOS 16.4+)

scrollBounceBehavior, introduced in iOS 16.4, dynamically controls scrolling behavior based on content size. Setting it to .basedOnSize disables scrolling when the content size is smaller than the container.

Swift
struct BasedOnSizeView: View {
    var body: some View {
        HStack {
            // Scrolling is enabled when content exceeds the container size
            ScrollView {
                ForEach(0 ..< 50) { idx in
                    Text("\(idx)").frame(maxWidth: .infinity)
                }
                .border(Color.blue)
            }

            // Scrolling is disabled when content is smaller than the container
            List(0 ..< 4) { idx in
                Text("\(idx)")
            }
            .border(Color.green)
        }
        .scrollBounceBehavior(.basedOnSize)
        .frame(width: 400, height: 500)
    }
}

2. Using ViewThatFits (iOS 16+)

ViewThatFits dynamically selects between multiple view configurations based on available space. By providing both scrollable and non-scrollable configurations, the system automatically chooses the appropriate view based on content size.

Swift
struct ScrollViewDemo: View {
    @State var step: CGFloat = 3
    var count: Int { Int(step) }

    var body: some View {
        VStack(alignment: .leading) {
            Text("Count: \(count)")
            Slider(value: $step, in: 3 ... 20, step: 1)

            ViewThatFits {
                content
                ScrollView(.horizontal, showsIndicators: true) {
                    content
                }
            }
        }
        .frame(width: 300)
        .border(.red)
    }

    var content: some View {
        HStack {
            ForEach(0 ..< count, id: \.self) { i in
                Rectangle()
                    .fill(.orange.gradient)
                    .frame(width: 30, height: 30)
                    .overlay(
                        Text(i, format: .number).foregroundStyle(.white)
                    )
            }
        }
    }
}

3. Using Introspect (For Older iOS Versions)

Introspect is a third-party library that provides access to UIKit components. It allows disabling scrolling on older iOS versions by manipulating UIScrollView properties.

Swift
struct BaseOnSizeTest: View {
    var body: some View {
        ScrollView {
            ForEach(0 ..< 10) {
                Text("\($0)")
            }
        }
        .introspect(.scrollView, on: .iOS(.v16, .v17, .v18)) { scrollView in
            // Disable scrolling if content size is smaller than the container size
            if scrollView.contentSize.height < scrollView.bounds.height {
                scrollView.isScrollEnabled = false
            }
        }
    }
}

Additional Resources