TL;DR: 在 SwiftUI 中,如果需要防止
ScrollView
或List
在内容小于容器时滚动,可根据 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
}
}
}
}