如何制作在 SwiftUI 中仍按住按钮时重复运行的 LongPressGesture?
How to make a LongPressGesture that runs repeatedly while the button is still being held down in SwiftUI?
我想在按住按钮时每 0.5 秒 运行 longPressGesture 中的代码。关于如何实现这个的任何想法?
import SwiftUI
struct ViewName: View {
var body: some View {
VStack {
Button(action: { } ) {
Image(systemName: "chevron.left")
.onTapGesture {
//Run code for tap gesture here
}
.onLongPressGesture (minimumDuration: 0.5) {
//Run this code every 0.5 seconds
}
}
}
}
哦,天哪,我不是真正的专家,但我最近遇到了类似的问题(检测按下和释放),我找到的解决方案不够优雅。如果有人展示更优雅的解决方案,我会很高兴,但这是我的怪物:
import SwiftUI
import Combine
struct ContentView: View {
@State private var ticker = Ticker()
@State private var isPressed: Bool = false
@State private var timePassed: TimeInterval?
var body: some View {
Button(action: {
// Action when tapped
NSLog("Tapped!")
}) {
Text(self.isPressed ? "Pressed for: \(String(format: "%0.1f", timePassed ?? 0))" : "Press and hold")
.padding()
.background(Capsule().fill(Color.yellow))
}
.onLongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity, pressing: { (value) in
self.isPressed = value
if value == true {
self.timePassed = 0
self.ticker.start(interval: 0.5)
}
}, perform: {})
.onReceive(ticker.objectWillChange) { (_) in
// Stop timer and reset the start date if the button in not pressed
guard self.isPressed else {
self.ticker.stop()
return
}
// Your code here:
self.timePassed = self.ticker.timeIntervalSinceStarted
}
}
}
/// Helper "ticker" that will publish regular "objectWillChange" messages
class Ticker: ObservableObject {
var startedAt: Date = Date()
var timeIntervalSinceStarted: TimeInterval {
return Date().timeIntervalSince(startedAt)
}
private var timer: Timer?
func start(interval: TimeInterval) {
stop()
startedAt = Date()
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in
self.objectWillChange.send()
}
}
func stop() {
timer?.invalidate()
}
deinit {
timer?.invalidate()
}
}
这需要解释:
- onTapGesture() 在这里不是必需的,因为这是 Button 默认情况下所做的,所以只需将 运行 所需的代码放在 action 块中就足够了;
- SwiftUI 中可用的手势数量有限,据我所知,制作新手势的唯一方法是组合现有手势;
- 没有一种手势是只要按下按钮就会一直执行代码,但LongPressGesture可能是最接近它的。但是,当分配的时间到期时,此手势会被识别(并结束),但您希望检测触摸持续时间,因此 minimumDuration: .infinity 参数;
- LongPressGesture 也会在触摸移开足够长的距离时结束,但是,这不是 Button 的工作方式 - 你可以走开然后 return 回来,只要你抬起触摸按钮视图的顶部,该手势将被识别为按下按钮。我们也应该在长按中复制这种行为,因此 maximumDistance: .infinity;
- 使用这些参数,LongPressGesture 将永远不会被识别,但是有一个 press 参数现在允许我们在按下开始和结束时收到通知;
- 可以使用某种计时器来每隔一段时间执行一个代码块;我从某处复制了这个 "ticker" ObservableObject。它必须是一个 ObservableObject,因为那样的话,我们可以在视图中订阅它的更新;
- 现在,当按下按钮时,我们启动自动收报机;
- 当股票报价时,我们使用 onReceive() 订阅者捕获它,这允许我们在每次报价时做一些事情。
类似的东西;再一次,我希望有人能告诉我更好的方法:)
祝你项目顺利!
–巴格兰
您可以使用计时器来完成此操作。使计时器在用户长按图像时启动,如果计时器达到 0,则可以添加两个操作:1. 将计时器重置回 0.5 秒,2.code 你想每 0.5 运行秒
struct ContentView: View {
@State var timeRemaining = 0.5
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
@State var userIsPressing = false //detecting whether user is long pressing the screen
var body: some View {
VStack {
Image(systemName: "chevron.left").onReceive(self.timer) { _ in
if self.userIsPressing == true {
if self.timeRemaining > 0 {
self.timeRemaining -= 0.5
}
//resetting the timer every 0.5 secdonds and executing code whenever //timer reaches 0
if self.timeRemaining == 0 {
print("execute this code")
self.timeRemaining = 0.5
}
}
}.gesture(LongPressGesture(minimumDuration: 0.5)
.onChanged() { _ in
//when longpressGesture started
self.userIsPressing = true
}
.onEnded() { _ in
//when longpressGesture ended
self.userIsPressing = false
}
)
}
}
}
今天早上我简单地清理了一下@Baglan 的“怪物”。
import Foundation
import SwiftUI
struct LongPressButton: View {
@ObservedObject var timer = PressTimer()
enum PressState {
case inactive
case pressing
case finished
}
@State private var pressState = PressState.inactive
var duration: Double = 2.0
var body: some View {
button
.onLongPressGesture(minimumDuration: duration, maximumDistance: 50, pressing: { (value) in
if value == true {
/// Press has started
self.pressState = .pressing
print("start")
self.timer.start(duration)
} else {
/// Press has cancelled
self.pressState = .inactive
print("stop")
self.timer.stop()
}
}, perform: {
/// Press has completed successfully
self.pressState = .finished
print("done")
})
}
var button: some View {
pressState == .pressing ? Text("Pressing - \(String(format: "%.0f", timer.percent))%")
: Text("Start")
}
}
class PressTimer: ObservableObject {
@Published var percent: CGFloat = 0
private var count: CGFloat = 0
private let frameRateHz: CGFloat = 60
private var durationSeconds: CGFloat = 2
var timer: Timer?
func start(_ duration: Double = 2.0) {
self.durationSeconds = CGFloat(duration)
let timerInterval: CGFloat = 1 / frameRateHz
timer = Timer.scheduledTimer(withTimeInterval: Double(timerInterval), repeats: true, block: { _ in
self.count += timerInterval
self.percent = self.count / self.durationSeconds * 100
})
}
func stop() {
self.count = 0
self.percent = 0
self.timer?.invalidate()
self.timer = nil
}
}
我想在按住按钮时每 0.5 秒 运行 longPressGesture 中的代码。关于如何实现这个的任何想法?
import SwiftUI
struct ViewName: View {
var body: some View {
VStack {
Button(action: { } ) {
Image(systemName: "chevron.left")
.onTapGesture {
//Run code for tap gesture here
}
.onLongPressGesture (minimumDuration: 0.5) {
//Run this code every 0.5 seconds
}
}
}
}
哦,天哪,我不是真正的专家,但我最近遇到了类似的问题(检测按下和释放),我找到的解决方案不够优雅。如果有人展示更优雅的解决方案,我会很高兴,但这是我的怪物:
import SwiftUI
import Combine
struct ContentView: View {
@State private var ticker = Ticker()
@State private var isPressed: Bool = false
@State private var timePassed: TimeInterval?
var body: some View {
Button(action: {
// Action when tapped
NSLog("Tapped!")
}) {
Text(self.isPressed ? "Pressed for: \(String(format: "%0.1f", timePassed ?? 0))" : "Press and hold")
.padding()
.background(Capsule().fill(Color.yellow))
}
.onLongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity, pressing: { (value) in
self.isPressed = value
if value == true {
self.timePassed = 0
self.ticker.start(interval: 0.5)
}
}, perform: {})
.onReceive(ticker.objectWillChange) { (_) in
// Stop timer and reset the start date if the button in not pressed
guard self.isPressed else {
self.ticker.stop()
return
}
// Your code here:
self.timePassed = self.ticker.timeIntervalSinceStarted
}
}
}
/// Helper "ticker" that will publish regular "objectWillChange" messages
class Ticker: ObservableObject {
var startedAt: Date = Date()
var timeIntervalSinceStarted: TimeInterval {
return Date().timeIntervalSince(startedAt)
}
private var timer: Timer?
func start(interval: TimeInterval) {
stop()
startedAt = Date()
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in
self.objectWillChange.send()
}
}
func stop() {
timer?.invalidate()
}
deinit {
timer?.invalidate()
}
}
这需要解释:
- onTapGesture() 在这里不是必需的,因为这是 Button 默认情况下所做的,所以只需将 运行 所需的代码放在 action 块中就足够了;
- SwiftUI 中可用的手势数量有限,据我所知,制作新手势的唯一方法是组合现有手势;
- 没有一种手势是只要按下按钮就会一直执行代码,但LongPressGesture可能是最接近它的。但是,当分配的时间到期时,此手势会被识别(并结束),但您希望检测触摸持续时间,因此 minimumDuration: .infinity 参数;
- LongPressGesture 也会在触摸移开足够长的距离时结束,但是,这不是 Button 的工作方式 - 你可以走开然后 return 回来,只要你抬起触摸按钮视图的顶部,该手势将被识别为按下按钮。我们也应该在长按中复制这种行为,因此 maximumDistance: .infinity;
- 使用这些参数,LongPressGesture 将永远不会被识别,但是有一个 press 参数现在允许我们在按下开始和结束时收到通知;
- 可以使用某种计时器来每隔一段时间执行一个代码块;我从某处复制了这个 "ticker" ObservableObject。它必须是一个 ObservableObject,因为那样的话,我们可以在视图中订阅它的更新;
- 现在,当按下按钮时,我们启动自动收报机;
- 当股票报价时,我们使用 onReceive() 订阅者捕获它,这允许我们在每次报价时做一些事情。
类似的东西;再一次,我希望有人能告诉我更好的方法:)
祝你项目顺利!
–巴格兰
您可以使用计时器来完成此操作。使计时器在用户长按图像时启动,如果计时器达到 0,则可以添加两个操作:1. 将计时器重置回 0.5 秒,2.code 你想每 0.5 运行秒
struct ContentView: View {
@State var timeRemaining = 0.5
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
@State var userIsPressing = false //detecting whether user is long pressing the screen
var body: some View {
VStack {
Image(systemName: "chevron.left").onReceive(self.timer) { _ in
if self.userIsPressing == true {
if self.timeRemaining > 0 {
self.timeRemaining -= 0.5
}
//resetting the timer every 0.5 secdonds and executing code whenever //timer reaches 0
if self.timeRemaining == 0 {
print("execute this code")
self.timeRemaining = 0.5
}
}
}.gesture(LongPressGesture(minimumDuration: 0.5)
.onChanged() { _ in
//when longpressGesture started
self.userIsPressing = true
}
.onEnded() { _ in
//when longpressGesture ended
self.userIsPressing = false
}
)
}
}
}
今天早上我简单地清理了一下@Baglan 的“怪物”。
import Foundation
import SwiftUI
struct LongPressButton: View {
@ObservedObject var timer = PressTimer()
enum PressState {
case inactive
case pressing
case finished
}
@State private var pressState = PressState.inactive
var duration: Double = 2.0
var body: some View {
button
.onLongPressGesture(minimumDuration: duration, maximumDistance: 50, pressing: { (value) in
if value == true {
/// Press has started
self.pressState = .pressing
print("start")
self.timer.start(duration)
} else {
/// Press has cancelled
self.pressState = .inactive
print("stop")
self.timer.stop()
}
}, perform: {
/// Press has completed successfully
self.pressState = .finished
print("done")
})
}
var button: some View {
pressState == .pressing ? Text("Pressing - \(String(format: "%.0f", timer.percent))%")
: Text("Start")
}
}
class PressTimer: ObservableObject {
@Published var percent: CGFloat = 0
private var count: CGFloat = 0
private let frameRateHz: CGFloat = 60
private var durationSeconds: CGFloat = 2
var timer: Timer?
func start(_ duration: Double = 2.0) {
self.durationSeconds = CGFloat(duration)
let timerInterval: CGFloat = 1 / frameRateHz
timer = Timer.scheduledTimer(withTimeInterval: Double(timerInterval), repeats: true, block: { _ in
self.count += timerInterval
self.percent = self.count / self.durationSeconds * 100
})
}
func stop() {
self.count = 0
self.percent = 0
self.timer?.invalidate()
self.timer = nil
}
}