💡 当内容小于容器时禁用 SwiftUI ScrollView 滚动

为您每周带来有关 Swift 和 SwiftUI 的精选资讯!

TL;DR: 在 SwiftUI 中,如果需要防止 ScrollViewList 在内容小于容器时滚动,可根据 iOS 版本选择适合的方法:在 iOS 16.4+ 中使用 scrollBounceBehavior(.basedOnSize),在 iOS 16+ 中通过 ViewThatFits 动态调整布局,而在更早的版本中,可以使用第三方库 Introspect 直接控制 UIScrollView 的滚动行为。

问题描述

ScrollView 默认允许用户滚动,无论内容尺寸是否小于容器尺寸,这可能导致不必要的交互问题,影响用户体验。

解决方案

以下方法可以在不同版本的 iOS 中解决此问题。

1. 使用 scrollBounceBehavior(iOS 16.4+)

scrollBounceBehavior 是 iOS 16.4 引入的 API,可根据内容尺寸动态控制滚动行为。当设置为 .basedOnSize 时,内容小于容器尺寸时将禁用滚动。

Swift
struct BasedOnSizeView: View {
    var body: some View {
        HStack {
            // 超过容器尺寸时启用滚动
            ScrollView {
                ForEach(0 ..< 50) { idx in
                    Text("\(idx)").frame(maxWidth: .infinity)
                }
                .border(Color.blue)
            }

            // 不超过容器尺寸时禁用滚动
            List(0 ..< 4) { idx in
                Text("\(idx)")
            }
            .border(Color.green)
        }
        .scrollBounceBehavior(.basedOnSize)
        .frame(width: 400, height: 500)
    }
}

2. 使用 ViewThatFits(iOS 16+)

ViewThatFits 根据可用空间动态选择视图布局。通过提供一个滚动容器和一个非滚动容器的视图结构,当内容小于容器尺寸时,自动选择非滚动视图。

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. 使用 Introspect(支持低版本 iOS)

Introspect 是一个第三方库,可以直接访问 UIKit 对象。在低版本中通过 UIScrollView 的属性禁用滚动。

Swift
struct BaseOnSizeTest: View {
    var body: some View {
        ScrollView {
            ForEach(0 ..< 10) {
                Text("\($0)")
            }
        }
        .introspect(.scrollView, on: .iOS(.v16, .v17, .v18)) { scrollView in
            // 根据内容尺寸禁用滚动
            if scrollView.contentSize.height < scrollView.bounds.height {
                scrollView.isScrollEnabled = false
            }
        }
    }
}

延伸阅读