Skip to content

Instantly share code, notes, and snippets.

@drosenstark
Last active May 1, 2023 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drosenstark/c839c309346903c9d4c4d30023aa5163 to your computer and use it in GitHub Desktop.
Save drosenstark/c839c309346903c9d4c4d30023aa5163 to your computer and use it in GitHub Desktop.
TwoStateButton.swift
struct TwoStateButton<Content: View>: View {
@State private var isPressed = false
let touchDownView: () -> Content
let touchUpView: () -> Content
let action: () -> ()
let touchDownAction: (() -> ())?
var body: some View {
Button(action: action, label: {
ZStack {
if isPressed {
touchDownView()
} else {
touchUpView()
}
}
}).buttonStyle(CustomButtonStyle(onPressed: {
isPressed = true
touchDownAction?()
}, onReleased: {
isPressed = false
}))
}
// https://stackoverflow.com/a/71714195/8047
private struct CustomButtonStyle: ButtonStyle {
var onPressed: () -> Void
var onReleased: () -> Void
// Wrapper for isPressed where we can run custom logic via didSet (or willSet)
@State private var isPressedWrapper: Bool = false {
didSet {
// new value is pressed, old value is not pressed -> switching to pressed state
if (isPressedWrapper && !oldValue) {
onPressed()
}
// new value is not pressed, old value is pressed -> switching to unpressed state
else if (oldValue && !isPressedWrapper) {
onReleased()
}
}
}
// return the label unaltered, but add a hook to watch changes in configuration.isPressed
func makeBody(configuration: Self.Configuration) -> some View {
return configuration.label
.onChange(of: configuration.isPressed, perform: { newValue in isPressedWrapper = newValue })
}
}
}
@drosenstark
Copy link
Author

In the end, in most cases the ON state won't show fast enough anyway... sad.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment