简体   繁体   English

DragGesture 阻止在 SwiftUI 中触摸滑块

[英]DragGesture blocks touching a Slider in SwiftUI

Let's take a look at this code:我们来看看这段代码:

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

I want to track touch down and end events on the slider, but this blocks the actual Slider from being touched.我想跟踪滑块上的触摸和结束事件,但这会阻止实际滑块被触摸。 In another words, DragGesture seems to consuming touches despite using simultaneousGesture.换句话说,尽管使用了同步手势,但 DragGesture 似乎消耗了触摸。 I've also tried adding GestureMask.all, but it didn't work either.我也试过添加 GestureMask.all,但也没有用。 How can I fix this?我怎样才能解决这个问题? I need to keep track of touch down/up, because this slider is used to seek an audio track, but also audio tracks updates the slider.我需要跟踪触地/触地,因为此滑块用于寻找音轨,而且音轨也会更新滑块。 However it shoudn't do it, when user is touching the slider (and sliderMoving flag should handle this).但是它不应该这样做,当用户触摸滑块时(并且滑块移动标志应该处理这个)。

Similar approach works with button tapping, but not with slider moving.类似的方法适用于按钮点击,但不适用于滑块移动。

edit:编辑:

I'm posting the final answer, thanks to @Marco Boerner for a big help:我正在发布最终答案,感谢@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))
    } 
}

The only downside is we have assume that knob has a fixed value, but because I'm setting slider height to 30 anyway, it works flawlessly in my case.唯一的缺点是我们假设旋钮具有固定值,但因为我无论如何都将滑块高度设置为 30,所以它在我的情况下完美无缺。

See if this solution works for you.看看这个解决方案是否适合你。 As mentioned in my comment it's basically overwriting the gesture reader of the slider completely.正如我在评论中提到的,它基本上完全覆盖了滑块的手势阅读器。 To get the width I used a geometry reader.为了获得宽度,我使用了几何阅读器。 You could probably even use .gesture or even .highPriorityGesture instead of simultaneousGesture Also depending where you place the GeometryReader you might have to use the .local coordinateSpace of the gesture.你也许甚至使用.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))
                    }
            )
            
        }
    }
}

Update with addition to comment below.更新除了下面的评论。 This optionally adjusts to the padding modifier and knob size.这可以选择性地调整到填充修饰符和旋钮大小。 Depending how the slider is setup different adjustments might be needed to get the exact position.根据滑块的设置方式,可能需要进行不同的调整才能获得准确的位置。 I'm not currently aware of a way to get the exact locations of individual parts of the slider.我目前不知道有什么方法可以获取滑块各个部分的确切位置。 A custom slider might solve this problem.自定义滑块可能会解决此问题。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM