简体   繁体   English

SwiftUI | 如果被 ScrollView 取消,则不会调用 DragGestures onEnded

[英]SwiftUI | DragGestures onEnded never called if canceled by ScrollView

I have the following problem.我有以下问题。

I have a View inside a ScrollView that I want to drag.我在ScrollView中有一个要拖动的View Now, everything works fine, the dragging occurs and the scrolling as well.现在,一切正常,发生拖动和滚动。 But once I try to do both, the dragOffset appears to stay, instead of resetting to its initial position.但是,一旦我尝试同时执行这两项操作,则dragOffset似乎会保持dragOffset ,而不是重置为其初始位置。

More so, onEnded of the DragGesture never happens.onEnded是, DragGesture onEnded永远不会发生。 How can I stop this from happening and reset the green Rectangle to its initial position?如何阻止这种情况发生并将绿色Rectangle重置为其初始位置?

Visual representation视觉表现

Dragging or scrolling works just fine.拖动或滚动工作得很好。

But whenever I do both, this happens.但是每当我同时做这件事时,就会发生这种情况。

Demo Code演示代码

struct ContentView: View {
    
    @State private var dragOffset: CGSize = .zero
    
    
    var body: some View {
        ScrollView {
            Rectangle()
                .frame(width: UIScreen.main.bounds.size.width, height: 300)
                .foregroundColor(.green)
                .offset(x: dragOffset.width)
        }
        .gesture(DragGesture()
            .onChanged { value in
                dragOffset = value.translation
            }
            .onEnded { value in
                dragOffset = .zero
            }
        )
        .animation(.default)
    }
}

Thank you!谢谢!

The solution is to use .updating with GestureState instead.解决方案是将.updatingGestureState一起使用。 It automatically sets the offset to its initial position, after the gesture got canceled for whatever reason.无论出于何种原因取消手势后,它都会自动将偏移设置为其初始位置。

That way, we are independent of calling onEnded to set it to its initial position which doesn't happen when you cancel the gesture by scrolling.这样,我们独立于调用onEnded将其设置为初始位置,当您通过滚动取消手势时不会发生这种情况。

Code:代码:

struct ContentView: View {
    
    @GestureState private var dragOffset: CGSize = .zero
    
    
    var body: some View {
        ScrollView {
            Rectangle()
                .frame(width: UIScreen.main.bounds.size.width, height: 300)
                .foregroundColor(.green)
                .offset(x: dragOffset.width)
        }
        .gesture(DragGesture()
            .updating($dragOffset) { value, state, transaction in
                state = value.translation
            }
        )
        .animation(.default)
    }
}

Note: You can of course still have an onChanged and onEnded block, you just don't have to set the offset to .zero any longer since it happens automatically.注意:您当然仍然可以有一个onChangedonEnded块,您只是不必.zero偏移量设置为.zero因为它会自动发生。

Update更新

If you are to use it inside ObservableObject class and want your dragOffset to be published, then here would be a solution I found .如果您要在ObservableObject类中使用它并希望发布 dragOffset,那么这将是我找到的解决方案

I think scrollView has its own gestures, such as UIScrollViewPanGestureRecognizer in UIScrollView, to add a DragGesture on scrollView will confuse it.我认为scrollView有自己的手势,比如UIScrollViewPanGestureRecognizer中的 UIScrollViewPanGestureRecognizer,在scrollView上添加DragGesture会混淆它。

you can change your code a little like:你可以稍微改变你的代码:

var body: some View {
    ScrollView {
        Rectangle()
            .frame(width: UIScreen.main.bounds.size.width, height: 300)
            .foregroundColor(.green)
            .offset(x: dragOffset.width, y: dragOffset.height)
            .gesture(DragGesture()
                .onChanged { value in self.dragOffset = value.translation }
                .onEnded { value in self.dragOffset = .zero }
        )
    }
    .animation(.default)
}

I think it can work well.我认为它可以很好地工作。

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

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