[英]Interaction of DragGesture and ScrollView in SwiftUI
In an app I'm working on, there is a part that has, mostly, a "forward" navigation – tapping on buttons would display the next slide.在我正在开发的应用程序中,有一部分主要是“前进”导航——点击按钮将显示下一张幻灯片。 However, a secondary "backward" navigation is also necessary.
然而,辅助“向后”导航也是必要的。 Here's the approach I've used:
这是我使用的方法:
import SwiftUI
struct Sample: View {
@State private var dragOffset: CGFloat = -100
var body: some View {
VStack {
Text("Perhaps a title")
ScrollView {
VStack {
Text("Some scrollable content is going to be here")
// ...
Button(action: {
// Go to the next slide
}) { Text("Next") }
}
}
Text("and, maybe, something else")
}
.overlay(
Image(systemName: "arrow.left").offset(x: dragOffset / 2),
alignment: .leading
)
.gesture(
DragGesture()
.onChanged{
self.dragOffset = $0.translation.width
}
.onEnded {
self.dragOffset = -100 // Hide the arrow
if $0.translation.width > 100 {
// Go to the previous slide
}
}
)
}
}
There is a small indicator (left arrow) that is, initially, hidden (dragOffset = -100).有一个小指标(左箭头),最初是隐藏的(dragOffset = -100)。 When the drag gesture begins, offset is fed into the dragOffset state variable and that, effectively, shows the arrow.
当拖动手势开始时,偏移量被输入到dragOffset状态变量中,并且有效地显示箭头。 When drag gesture ends, the arrow is hidden again and, if a certain offset is reached, the previous slide is displayed.
当拖动手势结束时,箭头再次隐藏,如果达到某个偏移量,则显示上一张幻灯片。
Works well enough, except, when the user scrolls the content in the ScrollView, this gesture is also triggered and updated for a while but then is, I assume, cancelled by the ScrollView and the "onEnded" is not called.工作得很好,除了当用户滚动 ScrollView 中的内容时,这个手势也会被触发并更新一段时间,但我认为,它被 ScrollView 取消并且不调用“onEnded”。 As a result, the arrow indicator stays on the screen.
结果,箭头指示符停留在屏幕上。
Hence the question: what is the correct way to do a gesture like that, that would work together with a ScrollView?因此,问题是:做这样的手势的正确方法是什么,可以与 ScrollView 一起使用? Is it even possible with the current state of SwiftUI?
SwiftUI 的当前状态是否有可能?
For such temporary states it is better to use GestureState
as it is automatically reset to initial state after gesture cancels/finished.对于这种临时状态,最好使用
GestureState
,因为它会在手势取消/完成后自动重置为初始状态。
So here is possible approach所以这是可能的方法
Demo:演示:
Code:代码:
struct Sample: View {
@GestureState private var dragOffset: CGFloat = -100
var body: some View {
VStack {
Text("Perhaps a title")
ScrollView {
VStack {
Text("Some scrollable content is going to be here")
// ...
Button(action: {
// Go to the next slide
}) { Text("Next") }
}
}
Text("and, maybe, something else")
}
.overlay(
Image(systemName: "arrow.left").offset(x: dragOffset / 2),
alignment: .leading
)
.gesture(
DragGesture()
.updating($dragOffset) { (value, gestureState, transaction) in
let delta = value.location.x - value.startLocation.x
if delta > 10 { // << some appropriate horizontal threshold here
gestureState = delta
}
}
.onEnded {
if $0.translation.width > 100 {
// Go to the previous slide
}
}
)
}
}
Note: dragOffset: CGFloat = -100
this might have different effect on different real devices, so probably it is better to calculate it explicitly.注意:
dragOffset: CGFloat = -100
这可能对不同的真实设备有不同的影响,所以可能最好明确计算。
If you don't want to use @GestureState
it's also possible to use DragGesture(minimumDistance: 0.0)
.如果您不想使用
@GestureState
也可以使用DragGesture(minimumDistance: 0.0)
。 This lets the DragGesture start before the scroll gesture is activated.这让 DragGesture 在滚动手势被激活之前启动。 Though this does seem like a bug on Apple's part to me.
虽然这对我来说似乎是苹果的一个错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.