每次点击时重置 DispatchQueue
Resetting DispatchQueue each time a tap occurs
我有一个简单的应用程序,旨在像这样工作:
- 应用加载默认图片
- 一旦收到点击,就会从列表中加载随机图像
- 如果 5 秒内没有收到点击,我们将重置为默认图像
我正在尝试使用 DispatchQueue 和 DispatchWorkItem 来完成此操作。我已经设法使上述功能正常工作 - 除非用户多次点击。
我假设这是因为我们每次点击时都会启动 "resetToOff" DispatchWorkItem 而不会重置 5 秒。
如果重置 DispatchQueue 时 5 秒没有收到触摸,如何将图像重置为默认图像?
这是我目前的情况:
import SwiftUI
struct PlayView : View {
@ObservedObject var viewRouter: ViewRouter
@State var imageName : String = "smiley"
var body: some View {
ZStack {
Color.black
.edgesIgnoringSafeArea(.all)
Image(imageName)
}
.gesture(
TapGesture()
.onEnded {
let resetToOff = DispatchWorkItem {
self.imageName = "smiley"
}
self.changeImage()
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5), execute: resetToOff)
}
)
// Activate the options menu
.onLongPressGesture(minimumDuration: 3) {
self.viewRouter.currentPage = "menuView"
}
}
func changeImage() {
let tempImageName : String = self.imageName
let list : Array = [
"smileyPink",
"smileyGreen",
"smileyRed",
"smileyBlue",
"smileyYellow"
]
self.imageName = list.randomElement() ?? ""
// Ensure that new image selection is not the same as previous image
while tempImageName == self.imageName {
self.imageName = list.randomElement() ?? ""
}
}
}
struct PlayView_Previews : PreviewProvider {
static var previews: some View {
PlayView(viewRouter: ViewRouter())
}
}
如有任何帮助,我们将不胜感激。
使用定时器会更简单,因为如果用户在定时器触发前点击,那么很容易取消 (invalidate
) 并再次启动定时器 (Timer.scheduledTimer
)在 5 秒结束时。
例如,这就是我的 LinkSame 应用程序的工作方式。有一个 10 秒 "move" 计时器。如果用户在上一次移动后的 10 秒内没有做出有效移动,则用户将失去 10 分并且计时器重新开始。如果用户 在上一次移动后的 10 秒内做出有效移动,用户将根据我们在计时器中的位置获得分数,并且计时器重新开始。这一切都是通过计时器完成的。
我认为修改您的代码非常容易,这样 resetToOff
仅在从 last tap:
过去 5 秒或更长时间后才执行某些操作
var lastTapped: DispatchTime
...
.gesture(
TapGesture()
.onEnded {
lastTapped = DispatchTime.now() // remember the time of last tap
然后:
let resetToOff = DispatchWorkItem {
if self.lastTapped + .seconds(5) <= DispatchTime.now() { // 5 sec passed from last tap
self.imageName = "smiley"
} // otherwise do nothing
}
由于设置和修改 lastTapped
都发生在主线程上,因此它是线程安全的。当然,这意味着您可能会将 "no work needed" 项目添加到主队列,但这可能影响很小。
我有一个简单的应用程序,旨在像这样工作:
- 应用加载默认图片
- 一旦收到点击,就会从列表中加载随机图像
- 如果 5 秒内没有收到点击,我们将重置为默认图像
我正在尝试使用 DispatchQueue 和 DispatchWorkItem 来完成此操作。我已经设法使上述功能正常工作 - 除非用户多次点击。
我假设这是因为我们每次点击时都会启动 "resetToOff" DispatchWorkItem 而不会重置 5 秒。
如果重置 DispatchQueue 时 5 秒没有收到触摸,如何将图像重置为默认图像?
这是我目前的情况:
import SwiftUI
struct PlayView : View {
@ObservedObject var viewRouter: ViewRouter
@State var imageName : String = "smiley"
var body: some View {
ZStack {
Color.black
.edgesIgnoringSafeArea(.all)
Image(imageName)
}
.gesture(
TapGesture()
.onEnded {
let resetToOff = DispatchWorkItem {
self.imageName = "smiley"
}
self.changeImage()
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5), execute: resetToOff)
}
)
// Activate the options menu
.onLongPressGesture(minimumDuration: 3) {
self.viewRouter.currentPage = "menuView"
}
}
func changeImage() {
let tempImageName : String = self.imageName
let list : Array = [
"smileyPink",
"smileyGreen",
"smileyRed",
"smileyBlue",
"smileyYellow"
]
self.imageName = list.randomElement() ?? ""
// Ensure that new image selection is not the same as previous image
while tempImageName == self.imageName {
self.imageName = list.randomElement() ?? ""
}
}
}
struct PlayView_Previews : PreviewProvider {
static var previews: some View {
PlayView(viewRouter: ViewRouter())
}
}
如有任何帮助,我们将不胜感激。
使用定时器会更简单,因为如果用户在定时器触发前点击,那么很容易取消 (invalidate
) 并再次启动定时器 (Timer.scheduledTimer
)在 5 秒结束时。
例如,这就是我的 LinkSame 应用程序的工作方式。有一个 10 秒 "move" 计时器。如果用户在上一次移动后的 10 秒内没有做出有效移动,则用户将失去 10 分并且计时器重新开始。如果用户 在上一次移动后的 10 秒内做出有效移动,用户将根据我们在计时器中的位置获得分数,并且计时器重新开始。这一切都是通过计时器完成的。
我认为修改您的代码非常容易,这样 resetToOff
仅在从 last tap:
var lastTapped: DispatchTime
...
.gesture(
TapGesture()
.onEnded {
lastTapped = DispatchTime.now() // remember the time of last tap
然后:
let resetToOff = DispatchWorkItem {
if self.lastTapped + .seconds(5) <= DispatchTime.now() { // 5 sec passed from last tap
self.imageName = "smiley"
} // otherwise do nothing
}
由于设置和修改 lastTapped
都发生在主线程上,因此它是线程安全的。当然,这意味着您可能会将 "no work needed" 项目添加到主队列,但这可能影响很小。