I'm developing a simple MIDI keyboard. Each piano key is a button. As soon you press it, it sends a "MIDI note ON" signal to a virtual device:
Button(action: {
MidiDevice.playNote("C")
}) {
Image(systemName: "piano-white-key")
}
It works fine. The latency is good and the user can play the key for just a fraction of a second or hold the button for longer notes. Now, how do I intercept the "user has lifted her finger" action in order to immediately send the MidiDevice.stopNote("C") event?
Here is possible solution (as far as I understood your goal) - to use ButtonStyle to detect isPressed state. Standard Button sends actions of tap UP, so we just add action handler for tap DOWN.
Tested with Xcode 12.4 / iOS 14.4
struct ButtonPressHandler: ButtonStyle {
var action: () -> ()
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.foregroundColor(configuration.isPressed ?
Color.blue.opacity(0.7) : Color.blue) // just to look like system
.onChange(of: configuration.isPressed) {
if $0 {
action()
}
}
}
}
struct TestButtonPress: View {
var body: some View {
Button(action: {
print(">> tap up")
}) {
Image(systemName: "piano-white-key")
}
.buttonStyle(ButtonPressHandler {
print("<< tap down")
})
}
}
In addition to Asperi's answer, you can create an extension which will make it more SwiftUI-style:
extension Button {
func onTapEnded(_ action: @escaping () -> Void) -> some View {
buttonStyle(ButtonPressHandler(action: action))
}
}
struct ContentView: View {
var body: some View {
Button(action: {
print(">> tap up")
}) {
Image(systemName: "piano-white-key")
}
.onTapEnded {
print("<< tap down")
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With