SwiftUI DragGesture onEnded 在某些情况下不会触发

kol*_*leS 7 xcode ios swiftui

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

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

它工作得很好,但如果用户在拖动时用另一根手指触摸卡片视图,然后将手指移离屏幕,卡片位置就会冻结,并且它不会移出屏幕或返回到初始位置。我调试了代码,结果发现在这种情况下该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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Wil*_*ake 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
        }
Run Code Online (Sandbox Code Playgroud)

通过更新“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.

                }
            }
Run Code Online (Sandbox Code Playgroud)

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