顾名思义,IsScrolling 提供了一个 ViewModifier,用于获取 SwiftUI 中 ScrollView 或 List 的当前滚动状态。IsScrolling 具有良好的向后和向前兼容性,因为它完全以 SwiftUI 原生方式实现。
两年前,当我开发 SwipeCell 时,我需要在可滚动组件(ScrollView、List)开始滚动时关闭已打开的侧滑菜单。这是通过使用 Introspect 将 Delegate 注入可滚动组件来实现的,并且我一直计划用更原生化的解决方案来替换它。
IsScrolling 有两种模式,每种模式都基于不同的实现原理
exclusion(排除)
仅支持 iOS,无需向可滚动组件的视图添加传感器,屏幕上只有一个可滚动组件
struct VStackExclusionDemo: View {
@State var isScrolling = false
var body: some View {
VStack {
ScrollView {
VStack {
ForEach(0..<100) { i in
CellView(index: i) // no need to add sensor in exclusion mode
}
}
}
.scrollStatusMonitor($isScrolling, monitorMode: .exclusion) // add scrollStatusMonitor to get scroll status
}
}
}
struct CellView: View {
@Environment(\.isScrolling) var isScrolling // can get scroll status in scrollable content
let index: Int
var body: some View {
Rectangle()
.fill(colors[index % colors.count].opacity(0.6))
.frame(maxWidth: .infinity, minHeight: 80)
.overlay(Text("ID: \(index) Scrolling: \(isScrolling ? "T" : "F")"))
}
}
common(通用)
适用于所有平台,同时监控屏幕上的多个可滚动组件,需要将传感器添加到可滚动组件的视图中。
struct ListCommonDemo: View {
@State var isScrolling = false
var body: some View {
VStack {
List {
ForEach(0..<100) { i in
CellView(index: i)
.scrollSensor() // Need to add sensor for each subview
}
}
.scrollStatusMonitor($isScrolling, monitorMode: .common)
}
}
}
对于 ScrollView + VStack (HStack) 这样的组合,只需向可滚动视图添加一个 scrollSensor。对于 List, ScrollView + LazyVStack (LazyHStack) 这样的组合,您需要为每个子视图添加一个 scrollSensor。
详情请查看 Demo
无论 IsScrolling 提供哪种监控模式,它都不能 100% 准确。毕竟,IsScrolling 是从某些外部现象推断出可滚动组件的当前滚动状态。已知问题如下。
.iOS(.v14),
.macOS(.v12),
.macCatalyst(.v14),
dependencies: [
.package(url: "https://github.com/fatbobman/IsScrolling.git", from: "1.0.0")
]
本库基于 MIT 许可发布。详情请查看 LICENSE。