如何检测 ScrollView 何时被拖动?
在我的 ScrollView 中,我有一个
@Binding scrollPositionOffset
变量,我用 .onChange(of:)
观察该变量,然后使用 ScrollViewReader.scrollTo()
以编程方式滚动到该位置。这很好用,但是当我直接滚动 ScrollView 时,我还需要更新 scrollPositionOffset
。我正在努力做到这一点,因为这会触发 .onChange(of:)
关闭并进入循环。
我的解决方案是仅当我将
ScrollViewReader.scrollTo()
变量设置为 false 时才有条件地调用 localScrolling
。我尝试使用 DragGesture.onChanged
和 .onEnded
进行设置,但这与导致滚动的拖动手势不同,因此 .onEnded
永远不会触发。
我认为我需要的是 ScrollView 的 @GestureRecognizer 类似于 UIScrollView 的
isDragging
或 isTracking
(我知道我可以使用 UIScrollView,但我不知道如何使用,这似乎可能需要更多工作! !我会接受一个告诉我如何将其放入 SwiftUIView 的答案)
上下文(如果有人对我的实际场景有更清晰的解决方案):
我有一个 ScrollView,我正在以编程方式滚动它以创建类似于 Xcode 中的 Minimap 视图的效果(即,我有一个与 ScrollView 相邻的缩小视图,拖动小地图会导致 ScrollView 滚动)。
当我使用小地图时,这非常有用,但我很难实现相反的情况:移动 ScrollView 的位置来更新小地图视图。
代码
@Binding var scrollPositionOffset: CGFloat
let zoomMultiplier:CGFloat = 1.5
var body: some View{
ScrollViewReader { scrollViewProxy in
GeometryReader{ geometry in
ScrollView {
ZStack(alignment:.top){
//The content of my ScrollView
MagnifierView()
.frame(height: geometry.size.height * zoomMultiplier)
//I'm using this as my offset reference
Rectangle()
.frame(height:10)
.alignmentGuide(.top) { _ in
geometry.size.height * zoomMultiplier * -scrollPositionOffset
}
.id("scrollOffset")
}
}
.onAppear(){
scrollViewProxy.scrollTo("scrollOffset", anchor: .top)
}
.onChange(of: scrollPositionOffset, perform: { _ in
//Only call .scrollTo() if the view isn't already being scrolled by the user
if !localScrolling {
scrollViewProxy.scrollTo("scrollOffset", anchor: .top)
}
})
.gesture(
DragGesture()
.onChanged{gesture in
localScrolling = true
let offset = gesture.location.y/(zoomMultiplier * geometry.size.height)
scrollPositionOffset = offset
}
.onEnded({gesture in
//Doesn't ever fire when scrolling
localScrolling = false
})
)
}
}
}
ScrollViewStyle
:
struct CustomScrollView: ScrollViewStyle {
@Binding var isDragging: Bool
func make(body: AnyView, context: Context) -> some View {
body
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
class Coordinator: ScrollViewCoordinator {
var parent: CustomScrollView
init(parent: CustomScrollView) {
self.parent = parent
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
parent.isDragging = false
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
parent.isDragging = true
}
}
}
struct TestView: View {
@State var isDragging = false
var body: some View {
ScrollView {
}.scrollViewStyle(CustomScrollView(isDragging: $isDragging))
}
}
2.0.0版本更新:
你现在要做的就是:
ScrollView
的状态
@ScrollState var state
scrollViewStyle
修饰符
.scrollViewStyle(.defaultStyle($state))
测试视图:
struct TestView: View {
@ScrollState var state
var body: some View {
ScrollView {
...
}.scrollViewStyle(.defaultStyle($state))
.onChange(of: state.isDragging) { newValue in
print(newValue)
}
}
}