如果被 ScrollView 取消,DragGestures onEnded 永远不会被调用

问题描述 投票:0回答:4

我有以下问题。

我想要拖动

View
内的
ScrollView
。 现在,一切正常,拖动和滚动都发生了。但一旦我尝试同时执行这两项操作,
dragOffset
似乎会保留下来,而不是重置到其初始位置。

更重要的是,

onEnded
DragGesture
永远不会发生。我怎样才能阻止这种情况发生并将绿色
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)
    }
}

谢谢!

ios swift swiftui
4个回答
20
投票

解决方案是使用

.updating
GestureState
代替。在手势因任何原因取消后,它会自动将偏移设置为其初始位置。

这样,我们就无需调用

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

注意:当然,您仍然可以拥有

onChanged
onEnded
块,只是不必再将偏移量设置为
.zero
,因为它会自动发生。

更新

如果您要在

ObservableObject
类中使用它并希望发布您的dragOffset,那么这将是我找到的解决方案


3
投票

解决这个问题的最好方法是在后台添加几何阅读器。更改 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 作为父级的视图中水平滚动。当我意识到当滚动视图接管时它没有回到“结束”位置时,问题就出现了。

为了解决这个问题,我实现了一个 GeometryReader 来监听整个视图的全局位置。如果

y
原点改变,它应该回到“结束”位置。

ScrollView {
  Content()
    .gesture(
      DragGesture(
        minimumDistance: 10,
        coordinateSpace: .global
      )
      .onChanged(handleChange)
      .onEnded(handleEnded)
    )
    .background {
      GeometryReader { geometry in
        Color.clear
          .onChange(of: geometry.frame(in: .global).origin.y) { _ in
            handleEnded()
          }
        }
      }
}


-1
投票

我认为

scrollView
有自己的手势,例如UIScrollView中的
UIScrollViewPanGestureRecognizer
,在
DragGesture
上添加
scrollView
会混淆它。

你可以像这样改变你的代码:

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

我觉得效果很好。

© www.soinside.com 2019 - 2024. All rights reserved.