问题描述
根据文档,.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
作为返回值
在其 枚举.
which has a .first(Bool)
and a .second(Bool, Bool?)
case in its Value
enum.
.first(Bool)
情况是 first LongPressGesture
尚未结束.
.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 状态
部分.
编辑 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 中停止长按手势时如何触发事件处理程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!