首页 > 解决方案 > SwiftUI DragGesture onEnded 在某些情况下不会触发

问题描述

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

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

它工作得很好,但是如果用户在拖动时用另一根手指触摸卡片视图,然后将他/她的手指从屏幕上移开,卡片位置会冻结,它既不会离开屏幕也不会回到初始位置。我调试了代码,结果发现在这种情况下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()
        }
    }
}

标签: iosxcodeswiftui

解决方案


由于 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.

                }
            }

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


推荐阅读