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
                    }
            )
            
        }
    }
}