根据文档,.OnEnded
事件处理程序将LongPressGesture
在成功检测到时触发。当用户在检测到手势后停止按下时,如何触发事件?
下面是一个例子:
用户按下例如 2 秒。
** 有东西出现 **
用户再过 2 秒后释放
** 东西消失了 **
小智 11
似乎这里可能有一个更优雅的解决方案,尽管它并不立即显现出来。
您可以使用最小距离为 0 的 DragGesture,而不是使用 LongPressGesture
@State var pressing = false
Text("Press Me").gesture(
DragGesture(minimumDistance: 0)
.onChanged({ _ in
pressing = true
})
.onEnded({ _ in
doThingWhenReleased()
})
)
Run Code Online (Sandbox Code Playgroud)
在此示例中,按下时按下将为 true,松开时将变为 false。当项目被释放时, onEnded 闭包将被调用。以下是将这个功能捆绑到 ViewModifier 中使用,但如果您希望它具有与其他手势类似的 api,那么作为独立的 Gesture() 可能会更好:
struct PressAndReleaseModifier: ViewModifier {
@Binding var pressing: Bool
var onRelease: () -> Void
func body(content: Content) -> some View {
content
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged{ state in
pressing = true
}
.onEnded{ _ in
pressing = false
onRelease()
}
)
}
}
extension View {
func pressAndReleaseAction(pressing: Binding<Bool>, onRelease: @escaping (() -> Void)) -> some View {
modifier(PressAndReleaseModifier(pressing: pressing, onRelease: onRelease))
}
}
Run Code Online (Sandbox Code Playgroud)
你可以这样使用它
struct SomeView: View {
@State var pressing
var body: some View {
Text(pressing ? "Press Me!" : "Pressed")
.pressAndReleaseAction(pressing: $pressing, onRelease: {
print("Released")
})
}
}
Run Code Online (Sandbox Code Playgroud)
唯一的缺点是,如果用户将鼠标拖动到按钮之外并释放,onRelease() 仍然会被触发。这可能会在 ViewModifier 内部更新以获得预期结果,用户可以将其拖动到外部以取消操作,但您可能必须使用 GeometryReader。
iSp*_*n17 10
我设法解决了它,但如果有人有更简单的解决方案,我会很乐意接受。
基本上我需要将 2 LongPressGesture
-s 链接在一起。
第一个将在长按 2 秒后生效 - 这是something
应该出现的时间。
第二个会在Double.infinity
一段时间后生效,这意味着它永远不会完成,所以用户可以按他们想要的时间。对于这种效果,我们只关心取消时的事件——这意味着用户停止按下。
@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)
Run Code Online (Sandbox Code Playgroud)
LongPressGesture
通过调用该.sequenced(before:)
方法对两个-s 进行排序时,您会得到一个
SequenceGesture<LongPressGesture, LongPressGesture>
作为返回值
它的枚举中有一个.first(Bool)
和一个.second(Bool, Bool?)
案例Value
。
这种
.first(Bool)
情况是第一个LongPressGesture
还没有结束的时候。这种
.second(Bool, Bool?)
情况是第一次LongPressGesture
结束时。
因此,当SequenceGesture
的值为 时.second(true, nil)
,这意味着第一个 Gesture 已完成而第二个尚未定义 - 这是应该显示某些内容的时候- 这就是我们将state
变量设置true
为该 case 内部的原因(该state
变量封装了isPressingDown
变量,因为它被作为.updating(_:body:)
方法的第一个参数)。
并且我们不需要做任何关于设置state
返回的事情,false
因为当使用该.updating(_:body:)
方法时,状态会返回到它的初始值——也就是false
——如果用户取消手势。这将导致“某物”的消失。(这里取消意味着我们在手势结束所需的最短秒数之前抬起手指——第二个手势是无限秒。)
因此,根据本文档的部分,请务必注意,在取消手势时不会调用该
.updating(_:body:)
方法的回调。Update Transient UI State
2021 年 3 月 24 日编辑:
在我看来,我遇到了更新 a@Published
属性的问题@ObservedObject
。由于.updating()
在重置时不会调用方法闭包,因此GestureState
您需要另一种方法来重置@Published
属性。解决该问题的方法是添加另一个名为的视图修饰符.onChange(of:perform:)
:
模型.swift:
class Model: ObservableObject {
@Published isPressedDown: Bool = false
private var cancellableSet = Set<AnyCancellable>()
init() {
//Do Something with isPressedDown
$isPressedDown
.sink { ... }
.store(in: &cancellableSet)
}
}
Run Code Online (Sandbox Code Playgroud)
查看.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
}
})
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1502 次 |
最近记录: |