ios - 带有点击和拖动手势的 SwiftUI ScrollView
问题描述
我正在尝试使用可以点击和拖动的元素来实现 ScrollView。它应该按以下方式工作:
- ScrollView 应该可以正常工作,因此向上/向下滑动不会干扰手势。
- 点击一个条目应该运行一些代码。如果有一个“点击指示器”那就太好了,这样用户就知道点击已被注册(困难在于点击指示器应该在 touch down时触发,而不是在 touch up时触发,并且应该处于活动状态直到手指松开)。
- 长按一个条目应该会激活一个拖动手势,因此可以移动项目。
下面的代码涵盖了所有这些要求(点击指示器除外)。但是,我不确定它为什么起作用,具体来说,为什么我需要使用 .highPriorityGesture 并且例如不能对 Tap Gesture 和 DragGesture 进行排序.sequenced(before: ...)
(这将阻止滚动)。
另外,我希望收到有关触地事件的通知(不是触地,请参阅 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)
}
}
解决方案
我尝试了几个选项,我认为结合sequenced
并simultaneously
允许两个手势同时运行。为了实现 onTouchDown,我使用了DragGesture
最小距离为 0 的。
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)
}
}
对于任何对此感兴趣的人来说,这是一个自定义滚动视图,不会被其中一条评论中提到的其他手势阻止。因为这不可能用标准的 ScrollView 来解决。
Github 上用于 SwiftUI 的 OpenScrollView
归功于
https://stackoverflow.com/a/59897987/12764795 http://developer.apple.com/documentation/swiftui/composing-swiftui-gestures https://www.hackingwithswift.com/books/ios-swiftui/how-在 swiftui 中使用手势
推荐阅读
- javascript - Leaflet _ NextJS - 我的应用程序奇怪地显示标记和弹出窗口
- c - `int kind : 19;` 是什么意思?
- javascript - 如何用另一个按钮而不是显示它的按钮隐藏 DIV?
- go - 在golang中运行多个条件函数
- css - 填充父div高度
- build - VMS 有哪些替代方案来管理不同的软件项目依赖项?
- flutter - 如何使用填充水平滚动到屏幕的最后/开始?
- sql - SELECT DATEPART, DATENAME or other? SQL
- asynchronous - 如何映射()一个流,直到它在 Dart 中被取消?
- azure - Question on azure service fabric application design