带有点击和拖动手势的 SwiftUI ScrollView
Posted
技术标签:
【中文标题】带有点击和拖动手势的 SwiftUI ScrollView【英文标题】:SwiftUI ScrollView with Tap and Drag gesture 【发布时间】:2020-10-28 13:21:43 【问题描述】:我正在尝试使用可以点击和拖动的元素来实现 ScrollView。它应该按以下方式工作:
-
ScrollView 应该可以正常工作,因此向上/向下滑动不会干扰手势。
点击一个条目应该运行一些代码。如果有一个“点击指示器”那就太好了,这样用户就知道点击已被注册(困难在于点击指示器应该在触摸 down 时触发,而不是在触摸向上,并且应该在手指松开之前一直处于活动状态。
长按条目应激活拖动手势,以便可以移动项目。
下面的代码涵盖了所有这些要求(点击指示器除外)。但是,我不确定 为什么 它有效,具体来说,为什么我需要使用 .highPriorityGesture 并且例如不能使用 .sequenced(before: ...)
对 Tap Gesture 和 DragGesture 进行排序(这将阻止滚动)。
另外,我希望收到有关触地事件的通知(不是触地,请参阅 2.)。我尝试使用 LongPressGesture() 而不是 TapGesture(),但这也会阻止 ScrollView 滚动,之后甚至不会触发 DragGesture。
有人知道如何实现吗?还是这就是 SwiftUI 的极限?如果是这样,是否有可能移植 UIKit 的东西来实现这一点(我也已经尝试过,但没有成功,ScrollView 的内容也应该是动态的,所以移植整个 ScrollView 可能很困难)?
感谢您帮助我!
struct ContentView: View
var body: some View
ScrollView()
ForEach(0..<5, id: \.self) i in
ListElem()
.highPriorityGesture(TapGesture().onEnded(print("tapped!")))
.frame(maxWidth: .infinity)
struct ListElem: View
@GestureState var dragging = CGSize.zero
var body: some View
Circle()
.frame(width: 100, height: 100)
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
.updating($dragging, body: t, state, _ in
state = t.translation
))
.offset(dragging)
【问题讨论】:
检查我的答案,看看这是否适合你,或者是否有什么事情没有按预期工作。 @leonboe1 有没有找到不阻止scrollView
滚动的解决方案?
很遗憾没有。
我发现了一些有用的东西***.com/a/59961959/11460922
已经试过了,但对我一点用都没有
【参考方案1】:
我尝试了几个选项,我认为sequenced
和simultaneously
的组合允许两个手势同时运行。为了实现 onTouchDown,我使用了最小距离为 0 的DragGesture
。
struct ContentView: View
var body: some View
ScrollView()
ForEach(0..<5, id: \.self) i in
ListElem()
.frame(maxWidth: .infinity)
struct ListElem: View
@State private var offset = CGSize.zero
@State private var isDragging = false
@GestureState var isTapping = false
var body: some View
// Gets triggered immediately because a drag of 0 distance starts already when touching down.
let tapGesture = DragGesture(minimumDistance: 0)
.updating($isTapping) _, isTapping, _ in
isTapping = true
// minimumDistance here is mainly relevant to change to red before the drag
let dragGesture = DragGesture(minimumDistance: 0)
.onChanged offset = $0.translation
.onEnded _ in
withAnimation
offset = .zero
isDragging = false
let pressGesture = LongPressGesture(minimumDuration: 1.0)
.onEnded value in
withAnimation
isDragging = true
// The dragGesture will wait until the pressGesture has triggered after minimumDuration 1.0 seconds.
let combined = pressGesture.sequenced(before: dragGesture)
// The new combined gesture is set to run together with the tapGesture.
let simultaneously = tapGesture.simultaneously(with: combined)
return Circle()
.overlay(isTapping ? Circle().stroke(Color.red, lineWidth: 5) : nil) //listening to the isTapping state
.frame(width: 100, height: 100)
.foregroundColor(isDragging ? Color.red : Color.black) // listening to the isDragging state.
.offset(offset)
.gesture(simultaneously)
对于任何对此感兴趣的人来说,这是一个自定义滚动视图,它不会被其中一个 cmets 中提到的其他手势阻止。因为这是标准 ScrollView 无法解决的。
OpenScrollView for SwiftUI on Github
归功于
https://***.com/a/59897987/12764795 http://developer.apple.com/documentation/swiftui/composing-swiftui-gestures https://www.hackingwithswift.com/books/ios-swiftui/how-to-use-gestures-in-swiftui
【讨论】:
哇,效果很好!谢谢!现在我只需要了解它的工作原理,感谢提供链接:D 我添加了几个 cmets。 :) 谢谢!但是,这种方法有一个警告:ScrollView 的滚动被阻塞。 ScrollView 真的很烦人。我想我会在几周后从头开始重新实现它。 你能解释一下滚动是什么时候被阻止的吗?您的意思是当您点击其中一个圆圈但在拖动之前? 我的意思是,如果您尝试通过向上/向下滑动来向上/向下移动滚动视图的内容,那么如果滑动手势开始于圆圈上,这将不起作用。跨度>以上是关于带有点击和拖动手势的 SwiftUI ScrollView的主要内容,如果未能解决你的问题,请参考以下文章
在 SwiftUI for macOS 应用程序中获取点击手势的鼠标/指针位置