DragGesture 阻止触摸 SwiftUI 中的滑块
DragGesture blocks touching a Slider in SwiftUI
让我们看一下这段代码:
@State private var progress: TimeInterval = 0
@State private var sliderMoving: Bool = false
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
seekToProgress(p: progress)
}).simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
print("gesture onChanged")
sliderMoving = true
}
.onEnded { gesture in
print("gesture onEnded")
sliderMoving = false
}
)
我想跟踪滑块上的触摸和结束事件,但这会阻止触摸实际的滑块。换句话说,尽管使用了 simultaneousGesture,但 DragGesture 似乎仍在消耗触摸。我也试过添加 GestureMask.all,但它也没有用。我怎样才能解决这个问题?我需要跟踪触摸 down/up,因为此滑块用于寻找音轨,而且音轨也会更新滑块。但是它不应该这样做,当用户触摸滑块时(并且 sliderMoving 标志应该处理这个)。
类似的方法适用于点击按钮,但不适用于滑块移动。
编辑:
我发布了最终答案,感谢@Marco Boerner 的大力帮助:
GeometryReader { geometry in
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
print("onEditingChanged") //not used
})
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
sliderMoving = true
updateProgress(x: gesture.location.x, width: geometry.size.width)
}
.onEnded { gesture in
sliderMoving = false
updateProgress(x: gesture.location.x, width: geometry.size.width)
seekToProgress(p: progress)
}
)
}
private func updateProgress(x: CGFloat, width: CGFloat) {
let knobWidth: CGFloat = 30
if x < knobWidth / 2 {
progress = TimeInterval(0)
} else if x >= width - knobWidth / 2 {
progress = TimeInterval(100)
} else {
let x2 = x - knobWidth / 2
let width2 = width - knobWidth
progress = TimeInterval(x2 / (width2 / 100))
}
}
唯一的缺点是我们假设旋钮具有固定值,但因为无论如何我都将滑块高度设置为 30,所以它在我的情况下完美无缺。
查看此解决方案是否适合您。正如我在评论中提到的,它基本上完全覆盖了滑块的手势 reader。为了获得宽度,我使用了几何 reader。您甚至可以使用 .gesture
甚至 .highPriorityGesture
而不是 simultaneousGesture
还取决于您放置 GeometryReader
的位置,您可能必须使用手势的 .local coordinateSpace
。
struct ContentView: View {
@State private var progress: TimeInterval = 0
@State private var sliderMoving: Bool = false
var body: some View {
GeometryReader { geometry in
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
}).simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
print("gesture onChanged")
sliderMoving = true
progress = TimeInterval(gesture.location.x / (geometry.size.width / 100))
}
.onEnded { gesture in
print("gesture onEnded")
sliderMoving = false
progress = TimeInterval(gesture.location.x / (geometry.size.width / 100))
}
)
}
}
}
更新并添加以下评论。这可以选择性地调整到填充修饰符和旋钮大小。根据滑块的设置方式,可能需要进行不同的调整才能获得准确的位置。我目前不知道有什么方法可以获取滑块各个部分的确切位置。自定义滑块可能会解决此问题。
struct ContentView6: View {
@State private var progress: TimeInterval = 0
@State private var sliderMoving: Bool = false
var body: some View {
GeometryReader { geometry in
let padding: CGFloat = 0 //optional in case padding needs to be adjusted.
let adjustment: CGFloat = padding + 15
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
})
.padding(padding)
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
sliderMoving = true
progress = TimeInterval( min(max((gesture.location.x - adjustment) / ((geometry.size.width - adjustment*2) / 100), 0), 100) )
print(progress)
}
.onEnded { gesture in
sliderMoving = false
}
)
}
}
}
让我们看一下这段代码:
@State private var progress: TimeInterval = 0
@State private var sliderMoving: Bool = false
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
seekToProgress(p: progress)
}).simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
print("gesture onChanged")
sliderMoving = true
}
.onEnded { gesture in
print("gesture onEnded")
sliderMoving = false
}
)
我想跟踪滑块上的触摸和结束事件,但这会阻止触摸实际的滑块。换句话说,尽管使用了 simultaneousGesture,但 DragGesture 似乎仍在消耗触摸。我也试过添加 GestureMask.all,但它也没有用。我怎样才能解决这个问题?我需要跟踪触摸 down/up,因为此滑块用于寻找音轨,而且音轨也会更新滑块。但是它不应该这样做,当用户触摸滑块时(并且 sliderMoving 标志应该处理这个)。
类似的方法适用于点击按钮,但不适用于滑块移动。
编辑:
我发布了最终答案,感谢@Marco Boerner 的大力帮助:
GeometryReader { geometry in
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
print("onEditingChanged") //not used
})
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
sliderMoving = true
updateProgress(x: gesture.location.x, width: geometry.size.width)
}
.onEnded { gesture in
sliderMoving = false
updateProgress(x: gesture.location.x, width: geometry.size.width)
seekToProgress(p: progress)
}
)
}
private func updateProgress(x: CGFloat, width: CGFloat) {
let knobWidth: CGFloat = 30
if x < knobWidth / 2 {
progress = TimeInterval(0)
} else if x >= width - knobWidth / 2 {
progress = TimeInterval(100)
} else {
let x2 = x - knobWidth / 2
let width2 = width - knobWidth
progress = TimeInterval(x2 / (width2 / 100))
}
}
唯一的缺点是我们假设旋钮具有固定值,但因为无论如何我都将滑块高度设置为 30,所以它在我的情况下完美无缺。
查看此解决方案是否适合您。正如我在评论中提到的,它基本上完全覆盖了滑块的手势 reader。为了获得宽度,我使用了几何 reader。您甚至可以使用 .gesture
甚至 .highPriorityGesture
而不是 simultaneousGesture
还取决于您放置 GeometryReader
的位置,您可能必须使用手势的 .local coordinateSpace
。
struct ContentView: View {
@State private var progress: TimeInterval = 0
@State private var sliderMoving: Bool = false
var body: some View {
GeometryReader { geometry in
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
}).simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
print("gesture onChanged")
sliderMoving = true
progress = TimeInterval(gesture.location.x / (geometry.size.width / 100))
}
.onEnded { gesture in
print("gesture onEnded")
sliderMoving = false
progress = TimeInterval(gesture.location.x / (geometry.size.width / 100))
}
)
}
}
}
更新并添加以下评论。这可以选择性地调整到填充修饰符和旋钮大小。根据滑块的设置方式,可能需要进行不同的调整才能获得准确的位置。我目前不知道有什么方法可以获取滑块各个部分的确切位置。自定义滑块可能会解决此问题。
struct ContentView6: View {
@State private var progress: TimeInterval = 0
@State private var sliderMoving: Bool = false
var body: some View {
GeometryReader { geometry in
let padding: CGFloat = 0 //optional in case padding needs to be adjusted.
let adjustment: CGFloat = padding + 15
Slider(value: $progress, in: 0 ... Double(100), onEditingChanged: { didChange in
})
.padding(padding)
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { gesture in
sliderMoving = true
progress = TimeInterval( min(max((gesture.location.x - adjustment) / ((geometry.size.width - adjustment*2) / 100), 0), 100) )
print(progress)
}
.onEnded { gesture in
sliderMoving = false
}
)
}
}
}