SwiftUI - How to make ScrollView not bounce when content is smaller than bounds

Viewed 1863

I'm trying to make my ScrollView:

  • Not bounce when the content is smaller than the screen
  • Bounce when the content overflows the screen

Here's my code:

struct ContentView: View {
    init() {
        UIScrollView.appearance().alwaysBounceVertical = false
    }
    var body: some View {
        ScrollView {
            Rectangle()
                .fill(Color.blue)
                .frame(height: 300) /// is smaller than the screen
                .padding()
        }
    }
}

I tried setting UIScrollView.appearance().alwaysBounceVertical = false, but the scroll view still bounces:

Swiping vertically on ScrollView, but still bounces

If I do UIScrollView.appearance().bounces = false, it stops bouncing. However, if I make the rectangle taller than the screen, it also stops bouncing (which I don't want).

Doesn't bounce (yay!) ... but doesn't bounce when the content overflows the screen
Swiping vertically on ScrollView with short rectangle doesn't bounce Swiping vertically on ScrollView with tall rectangle also doesn't bounce

How can I disable bouncing, but only when the content is smaller than scroll view's bounds?

2 Answers

Well, when using SwiftUI-Introspect, setting alwaysBounceVertical to false actually works. I'm not exactly sure why setting the appearance as you did doesn't work though...

Anyway, here is some working example code:

struct ContentView: View {
    
    @State private var count = 5
    
    var body: some View {
        VStack {
            Picker("Count", selection: $count) {
                Text("5").tag(5)
                Text("100").tag(100)
            }
            .pickerStyle(SegmentedPickerStyle())
            
            ScrollView {
                VStack {
                    ForEach(1 ... count, id: \.self) { i in
                        Text("Item: \(i)")
                    }
                }
                .frame(maxWidth: .infinity)
            }
            .introspectScrollView { scrollView in
                scrollView.alwaysBounceVertical = false
            }
        }
    }
}

Result (unfortunately you can't see my mouse trying to drag the shorter list):

Result

Made a small extension based on George's answer using SwiftUI-Introspect:

extension View {
    func disableBouncingWhenNotScrollable() -> some View {
        introspectScrollView { scrollView in
            scrollView.alwaysBounceVertical = false
            scrollView.alwaysBounceHorizontal = false
        }
    }
}

Usage:

ScrollView {
    // content
}
.disableBouncingWhenNotScrollable()
Related