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

26

我有以下问题。

ScrollView中,我想拖动一个View。现在,一切都很好,拖动发生了,滚动也发生了。但是一旦我尝试做两者,dragOffset似乎会保持不变,而不是重置到其初始位置。

更多地是,DragGestureonEnded从未发生。如何阻止这种情况发生并将绿色的Rectangle重置为其初始位置?

可视化表示

拖动或滚动工作正常。

但是每当我两者兼具时,就会发生这种情况。

演示代码

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

谢谢你!


我通过在 DragGesture 中添加最小拖动距离来改善了它,像这样:DragGesture(minimumDistance: 20)。这对我有所帮助,但只是一个半成品修复,因为现在必须拖动超过20才能激活手势。 - Kai Zheng
DragGesture(minimumDistance: 1)同样有效。 - Hai Feng Kao
3个回答

15
解决方法是使用 .updatingGestureState。它会自动将偏移量设置为初始位置,即使手势因任何原因被取消。
这样,我们不需要调用onEnded将其设置回到初始位置,因为当您通过滚动取消手势时,这种情况不会发生。

代码:

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)
    }
}
注意:您当然仍然可以使用 onChangedonEnded 块,但是由于它会自动发生,因此您不再需要将偏移量设置为 .zero

更新

如果你想在 ObservableObject 类中使用它并且希望发布你的 dragOffset,则这里有一个解决方案


4
很遗憾,ScrollView仍存在一个问题,即无论你添加了哪种策略,一旦开始滚动,它就会立即取消任何拖动手势。因此,updating将被调用一次,然后手势就会被取消,不会再被调用。 - CouchDeveloper
@CouchDeveloper 我的视图位于一个ScrollView中,似乎没有任何问题。我将我的视图包装在一个按钮内,手势似乎可以很好地传播(这是一个笨拙的解决方法,但更新方法在我的ScrollView中非常有效)。 - yuroyami

1

解决此问题的最佳方案是在后台添加几何阅读器。当滚动时,通过更改geo.frame(in:.global).minY更新偏移量。此方法使用少量处理器(我发现CPU使用率增加了约2%)。

在视图中的某个位置添加以下代码:

.background(Group{
    GeometryReader{ geo in
        HStack{
             
        }
        .onChange(of: geo.frame(in: .global).minY) { _ in
            // update the offset
        }
    }
}

0

我认为scrollView有自己的手势,例如UIScrollView中的UIScrollViewPanGestureRecognizer,在scrollView上添加一个DragGesture会使其混淆。

你可以稍微修改一下你的代码:

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

我认为它可以很好地工作。


1
谢谢你的回答!不幸的是,这并没有解决我的问题。 - Kai Zheng
你能告诉我为什么手势不能添加到“矩形”而不是“滚动视图”中吗?也许我们可以考虑另一种解决方案。 - leorider
可以,但是它没有改变任何东西,我现在有一个解决方案了,谢谢! - Kai Zheng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接