I'd like to run the code in the longPressGesture every 0.5 seconds while the button is being held down. Any ideas on how to implement this?
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
}
}
}
}
Oh boy, I'm not really an expert but I've had a similar problem (detecting pressing and releasing) recently and the solution I've found is less than elegant. I'd love if someone show a more elegant solution but here's my monstrosity:
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()
}
}
This requires an explanation:
Something like that; again, I'd love someone to show me a better way:)
Good luck with your project!
–Baglan
You can do this by using timer. Make the timer starts when the user long pressed the image, and if the timer reaches 0, you can add two actions: 1. resetting the timer back to 0.5 seconds and 2.code you want to run every 0.5 seconds
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
}
)
}
}
}
I simply cleaned up @Baglan 's "monstrosity" a bit this morning.
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
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.