Skip to content

Instantly share code, notes, and snippets.

@bradhowes
Last active January 16, 2021 11:53
Show Gist options
  • Save bradhowes/0c5fd1f571a4331f4f093a860e2c2251 to your computer and use it in GitHub Desktop.
Save bradhowes/0c5fd1f571a4331f4f093a860e2c2251 to your computer and use it in GitHub Desktop.
Xcode playground showing SwiftUI custom button that repeatedly fires an action while touched
import SwiftUI
import PlaygroundSupport
class Model: ObservableObject {
@Published var value: Double = 5.0
}
struct RepeatButton: View {
private let title: String
private let systemImage: String
private let interval: TimeInterval
private let action: () -> Void
@State private var timer: Timer? = nil
init(_ title: String, systemImage: String, interval: TimeInterval = 0.4, action: @escaping () -> Void) {
self.title = title
self.systemImage = systemImage
self.interval = interval
self.action = action
}
var body: some View {
Label(title, systemImage: systemImage)
.padding(8.0)
.background(Color.black.brightness(timer != nil ? 0.60 : 0.50))
.cornerRadius(8.0)
.gesture(makeGesture())
}
private func makeGesture() -> some Gesture {
DragGesture(minimumDistance: 0)
.onChanged { _ in
guard timer == nil else { return }
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in action() }
action()
}
.onEnded { _ in
timer?.invalidate()
timer = nil
}
}
}
struct ContentView: View {
@ObservedObject var model: Model
var body: some View {
VStack {
VStack(spacing: 8) {
Text("Value")
Text(String(format: "%.2f", model.value))
.font(.title)
}
.padding()
VStack {
Text("Move slider to modify the model value.")
Slider(value: $model.value, in: 0...10)
}
.padding()
.border(Color.white)
VStack {
Text("Press buttons to step change the model value.")
HStack {
RepeatButton("Down", systemImage: "chevron.left", action: decrementAction)
RepeatButton("Up", systemImage: "chevron.right", action: incrementAction)
}
.labelStyle(IconOnlyLabelStyle())
}
.padding()
.border(Color.white)
}
}
private func decrementAction() { changeValue(by: -1.0) }
private func incrementAction() { changeValue(by: 1.0) }
private func changeValue(by: Double) { model.value = min(max(model.value + by, 0), 10) }
}
let view = ContentView(model: Model())
PlaygroundPage.current.setLiveView(view)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment