【问题标题】:SwiftUI DragGesture onEnded does not fire in some casesSwiftUI DragGesture onEnded 在某些情况下不会触发
【发布时间】:2021-06-05 11:52:38
【问题描述】:

在我的应用中,我有一个可以向左或向右拖动的可滑动卡片视图。

当用户向左或向右拖动卡片时,卡片的水平偏移会更新。当用户释放拖动时,卡片会移出屏幕(向左或向右,取决于拖动方向),或者如果水平偏移没有超过阈值,卡片会回到初始位置。

效果很好,但是如果用户在拖动时用另一根手指触摸卡片视图,然后将他/她的手指从屏幕上移开,卡片位置会冻结,它既不会移出屏幕也不会返回到初始位置。我调试了代码,结果发现在这种情况下DragGesture().onEnded 事件不会触发。

我正在寻找有关如何检测这种情况的任何提示。

代码如下:

如果我有类似isTouchingScreen 的状态,我就能解决这个问题。

编辑:这是一个问题出现的最小示例。

import SwiftUI



struct ComponentPlayground: View {
    @State private var isDragging: Bool = false
    @State private var horizontalOffset: CGFloat = .zero
    
    var background: Color {
        if abs(horizontalOffset) > 100 {
            return horizontalOffset < 0 ? Color.red : Color.green
        } else {
            return Color.clear
        }
    }
    
    var body: some View {
        GeometryReader { geometry in
            Color.white
                .cornerRadius(15)
                .shadow(color: Color.gray, radius: 5, x: 2, y: 2)
                .overlay(background.cornerRadius(15))
                .rotationEffect(.degrees(Double(horizontalOffset / 10)), anchor: .bottom)
                .offset(x: horizontalOffset, y: 0)
                .gesture(
                    DragGesture()
                        .onChanged { gesture in
                            self.isDragging = true
                            self.horizontalOffset = gesture.translation.width
                        }
                        .onEnded { gesture in
                            self.isDragging = false
                            
                            if abs(horizontalOffset) > 100 {
                                withAnimation {
                                    self.horizontalOffset *= 5
                                }
                            } else {
                                withAnimation {
                                    self.horizontalOffset = .zero
                                }
                            }
                            
                        }
                )
                .frame(width: 300, height: 500)
        }
    }
}

struct ComponentPlayground_Previews: PreviewProvider {
    static var previews: some View {
        VStack {
            Spacer()
            HStack(alignment: .center) {
                ComponentPlayground()
            }
            .frame(width: 300, height: 500)
            Spacer()
        }
    }
}

【问题讨论】:

  • 如果你使用.gesture(...)而不是.highPriorityGesture(...)会触发吗?
  • 如果没有.highPriorityGesture(...),整个视图将不起作用,因为CardView 嵌入在ZStack 中(下面还有其他CardViews)并且所有内容都包含在@987654332 中@。对不起,也许我应该在最初的问题中提到它..
  • @George_E 不知道为什么投反对票。我验证了如果那里的DragGesture 没有高优先级它不起作用(请检查上面的更新代码)。只需运行此代码并在拖动时触摸卡片,它就会冻结。
  • 投反对票的不是我……我不知道他们的原因
  • @WilhelmLake 到目前为止没有更新。一位苹果工程师在该反馈线程中要求我检查问题是否仍然存在。不幸的是,经过检查,它仍然没有修复。我还检查了最新的 iOS 15,问题仍然存在。

标签: ios xcode swiftui


【解决方案1】:

由于 Apple 没有对此问题进行更新,因此我将分享我针对此问题的解决方法。 解决方法仍然不提供类似于 UIKit 中的 UIPanGesture 的拖动功能,但仍然...

首先我创建了两个 GestureStates 变量并在我的 DragGesture 中不断更新它们:

@GestureState private var stateOffset:      CGSize = CGSize.zero
@GestureState private var isGesturePressed: Bool   = false


        let dragGesture = DragGesture()
        .updating($stateOffset) { value, gestureState, transaction in
            gestureState = CGSize(width: value.translation.width, height: value.translation.height)
        }
        .updating($isGesturePressed) { value, gestureState, transaction in
            gestureState = true
        }

通过更新“isGesturePressed”(通过在手势处于活动状态时使其为真)我会知道手势是否实际被按下。当它按下时,我用 stateOffset 做我的动画代码:

         SomeView()
        .offset(someCustomOffset)
        .gesture(dragGesture)
        .onChange(of: stateOffset) { _ in
            if isGesturePressed {
                withAnimation { Your Animation Code here

                }
            } else {
                withAnimation { Your Animation Code here for when the gesture is no longer pressed. This is basically my .onEnd function now.

                }
            }

正如我上面所说的。它不会提供通常的 UX(例如 Twitter 侧边菜单),导致我们的动画在正在进行的动画期间当另一个手指按下屏幕时立即结束。但至少它不再卡在中途。 肯定有更优雅和更好的书面解决方法,但我希望它可以帮助某人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多