.onKeyPress action closures exhibit correct behaviour when modifying a State variable defined with the view it operates on.
If however, the closure attempts to update a bound variable (defined with @Binding) then the variable is only ever updated once, and does not continue to fire when the corresponding key is held down.
Apple feedback filed: FB14751737
import SwiftUI
struct ContentView: View {
@State var doubleValue: Double = 1.0
var body: some View {
VStack {
Text("Focus the value and press and hold either <- left arrow or right arrow ->")
HStack{ Text("Repeats don't work: "); DoesntWork(doubleValue: $doubleValue) }
}.monospaced()
}
}
struct DoesntWork: View {
@Binding var doubleValue: Double
var body: some View {
Text("(doubleValue)")
.focusable()
.onKeyPress( keys: [.leftArrow,.rightArrow], phases: [.down, .repeat] ) {keyPress in
if keyPress.key == .leftArrow { doubleValue -= 1} else { doubleValue += 1} // only works once - ignores repeats
return .handled
}
}
}
Strangely enough, wrapping the update to the bound variable in a Task closure restores the expected behaviour:
import SwiftUI
struct ContentView: View {
@State var doubleValue: Double = 1.0
var body: some View {
VStack {
Text("Focus one value and press and hold either <- left arrow or right arrow ->")
HStack{ Text("Repeats don't work: "); DoesntWork(doubleValue: $doubleValue) }
HStack{ Text("Repeats work: "); Works(doubleValue: $doubleValue) }
}.monospaced()
}
}
struct DoesntWork: View {
@Binding var doubleValue: Double
var body: some View {
Text("(doubleValue)")
.focusable()
.onKeyPress( keys: [.leftArrow,.rightArrow], phases: [.down, .repeat] ) {keyPress in
if keyPress.key == .leftArrow { doubleValue -= 1} else { doubleValue += 1} // only works once - ignores repeats
return .handled
}
}
}
/// Very hacky fix to the apparent bug attempting to modify a binding from an onKeyPress event.
struct Works: View {
@Binding var doubleValue: Double
var body: some View {
Text("(doubleValue)")
.focusable()
.onKeyPress( keys: [.leftArrow,.rightArrow], phases: [.down, .repeat] ) { keyPress in
Task { // why does this make it work??
if keyPress.key == .leftArrow { doubleValue -= 1} else { doubleValue += 1}
}
return .handled
}
}
}