当用户在 SwiftUI 中停止长按手势时如何触发事件处理程序? [英] How to fire event handler when the user STOPS a Long Press Gesture in SwiftUI?

查看:47
本文介绍了当用户在 SwiftUI 中停止长按手势时如何触发事件处理程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据文档,.OnEnded 事件处理程序将在成功检测到 LongPressGesture 时触发.当用户在检测到手势后停止按压时如何触发事件?

Based on the documentation, the .OnEnded event handler will fire when the LongPressGesture has been successfully detected. How can I fire an event when the user stops pressing after the gesture has been detected?

这是一个例子:

  • 用户按下例如2 秒.

  • User presses for e.g. 2 seconds.

** 有东西出现 **

** Something appears **

用户再过 2 秒后释放

User releases after another 2 seconds

** 东西消失了 **

** That something disappears **

推荐答案

我设法解决了它,但如果有人有更简单的解决方案,我会很乐意接受.

I managed to solve it, although if anyone has an easier solution I would gladly accept.

基本上我需要将 2 个 LongPressGesture-s 链接在一起.

Basically I need to chain 2 LongPressGesture-s together.

第一个将在长按 2 秒后生效 - 这是 something 应该出现的时候.

The first one will take effect after a 2 second long press - this is when the something should appear.

第二个会在Double.infinity时间后生效,意味着它永远不会完成,所以用户可以按他们想要的时间.对于这种效果,我们只关心事件取消时 - 意味着用户停止按下.

The second one will take effect after Double.infinity time, meaning that it will never complete, so the user can press as long as they want. For this effect, we only care about the event when it is cancelled - meaning that the user stopped pressing.

@GestureState private var isPressingDown: Bool = false

[...]

aView.gesture(LongPressGesture(minimumDuration: 2.0)
    .sequenced(before: LongPressGesture(minimumDuration: .infinity))
    .updating($isPressingDown) { value, state, transaction in
        switch value {            
            case .second(true, nil): //This means the first Gesture completed
                state = true //Update the GestureState
            default: break
        }
    })
    .
[...]

something.opacity(isPressingDown ? 1 : 0)

当通过调用 LongPressGesture-s 进行排序时>.sequenced(before:) 方法,你得到一个

When sequencing two LongPressGesture-s by calling the .sequenced(before:) method, you get a

SequenceGesture 作为返回值

在其 Value 枚举.

which has a .first(Bool) and a .second(Bool, Bool?) case in its Value enum.

.first(Bool) 情况是 first LongPressGesture 尚未结束.

The .first(Bool) case is when the first LongPressGesture hasn't ended yet.

.second(Bool, Bool?) 的情况是 first LongPressGesture 已经结束.

The .second(Bool, Bool?) case is when the first LongPressGesture has ended.

因此,当 SequenceGesture 的值为 .second(true, nil) 时,这意味着第一个手势已完成而第二个手势尚未定义 - 这是当应该显示 something - 这就是为什么我们在这种情况下将 state 变量设置为 true 的原因(state 变量封装了 isPressingDown 变量,因为它作为第一个参数提供给 .updating(_:body:) 方法).

So when the SequenceGesture's value is .second(true, nil), that means the first Gesture has completed and the second is yet undefined - this is when that something should be shown - this is why we set the state variable to true inside that case (The state variable encapsulates the isPressingDown variable because it was given as first parameter to the .updating(_:body:) method).

而且我们无需将 state 设置回 false,因为在使用 .updating(_:body:) 时code> 方法状态返回到其初始值 - 这是 false - 如果用户取消手势.这将导致某物"的消失.(这里取消意味着我们在手势结束所需的最短秒数之前抬起手指——第二个手势是无限秒.)

And we don't have to do anything about setting the state back to false because when using the .updating(_:body:) method the state returns to its initial value - which was false - if the user cancels the Gesture. Which will result in the disappearance of "something". (Here cancelling means we lift our finger before the minimum required seconds for the Gesture to end - which is infinity seconds for the second gesture.)

因此请务必注意 .updating(_:body:) 方法的回调在取消手势时不会调用,根据 this 文档的更新瞬态 UI 状态 部分.

So it is important to note that the .updating(_:body:) method's callback is not called when the Gesture is cancelled, as per this documentation's Update Transient UI State section.


编辑 2021 年 3 月 24 日:

在我看来,我遇到了更新 @ObservedObject@Published 属性的问题.由于在重置 GestureState 时不会调用 .updating() 方法闭包,因此您需要另一种方法来重置 @Published 属性.解决该问题的方法是添加另一个名为 .onChange(of:perform:):

I ran into the problem of updating an @Published property of an @ObservedObject in my view. Since the .updating() method closure is not called when resetting the GestureState you need another way to reset the @Published property. The way to solve that issue is adding another View Modifier called .onChange(of:perform:):

Model.swift:

Model.swift:

class Model: ObservableObject {
    @Published isPressedDown: Bool = false
    
    private var cancellableSet = Set<AnyCancellable>()

    init() {
        //Do Something with isPressedDown
        $isPressedDown
            .sink { ... }
            .store(in: &cancellableSet)
    }
}

View.swift:

View.swift:

@GestureState private var _isPressingDown: Bool = false
@ObservedObject var model: Model

[...]

aView.gesture(LongPressGesture(minimumDuration: 2.0)
    .sequenced(before: LongPressGesture(minimumDuration: .infinity))
    .updating($_isPressingDown) { value, state, transaction in
        switch value {            
            case .second(true, nil): //This means the first Gesture completed
                state = true //Update the GestureState
                model.isPressedDown = true //Update the @ObservedObject property
            default: break
        }
    })
    .onChange(of: _isPressingDown) { value in
        if !value {
            model.isPressedDown = false //Reset the @ObservedObject property
        }
    })

这篇关于当用户在 SwiftUI 中停止长按手势时如何触发事件处理程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆