简体   繁体   English

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

[英]SwiftUI DragGesture onEnded does not fire in some cases

In my app I have a swipeable card view that can be dragged left or right.在我的应用程序中,我有一个可以向左或向右拖动的可滑动卡片视图。

When the user drags the card left or right, the card's horizontal offset is updated.当用户向左或向右拖动卡片时,卡片的水平偏移会更新。 When the user releases the drag, the card moves off the screen (either left or right, depending on the drag direction) or the card goes back to the initial position if the horizontal offset did not exceed the threshold.当用户释放拖动时,卡片移出屏幕(向左或向右,取决于拖动方向),或者如果水平偏移未超过阈值,卡片将返回初始 position。

It works well, but if the user touches the card view with another finger while dragging and then takes his/her fingers off screen, the card position freezes, and it doesn't either move off the screen or go back to the initial position.它工作得很好,但是如果用户在拖动时用另一根手指触摸卡片视图,然后将他/她的手指从屏幕上移开,卡片 position 会冻结,它既不会离开屏幕,也不会离开屏幕或 go 回到最初的 Z47576FE07FD492A8DEAZ060 I debugged the code and it turns out that in that case the DragGesture().onEnded event does not fire.我调试了代码,结果发现在这种情况下DragGesture().onEnded事件不会触发。

在此处输入图像描述

I am looking for any hints on how I can detect this situation.我正在寻找有关如何检测这种情况的任何提示。

Here is the code:这是代码:

If I had something like isTouchingScreen state, I would be able to solve this.如果我有类似isTouchingScreen state 之类的东西,我就能解决这个问题。

EDIT: Here is a minimal example where the problem manifests itself.编辑:这是问题表现出来的一个最小示例。

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

Since there is no update on the issue from Apple, I will share my workaround for this problem.由于 Apple 没有对此问题进行更新,因此我将分享解决此问题的方法。 Workaround still does not provide drag functionality similar to UIPanGesture from UIKit, but still...解决方法仍然不提供类似于来自 UIKit 的 UIPanGesture 的拖动功能,但仍然......

First I've created two GestureStates variables and keep updating them in my DragGesture:首先,我创建了两个 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
        }

By updating "isGesturePressed"(by making it true while gesture is active) I will know if the gesture is actually pressed or not.通过更新“isGesturePressed”(通过在手势处于活动状态时使其为真),我将知道手势是否实际被按下。 And while it pressed, I do my animation code with stateOffset:当它按下时,我使用 stateOffset 执行 animation 代码:

         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.

                }
            }

As I said above.正如我上面所说。 It will not provide the usually UX (eg Twitter side menu), cause our animation just immeadiatly ends when another finger presses the screen during the on-going animation.它不会提供通常的 UX(例如 Twitter 侧边菜单),导致我们的 animation 在正在进行的 Z6F1C25ED1523962F1BBF9DEE9BE5092B 过程中当另一个手指按下屏幕时立即结束。 But at least it is no longer stuck mid-way.但至少它不再卡在中途。 There are definitely more elegant and better written workarounds for this, but I hope it may help someone.肯定有更优雅和更好的书面解决方法,但我希望它可以帮助某人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM