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+, andIntrospect
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.
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.
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.
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
}
}
}
}