Hi everyone,
I'm building a macOS SwiftUI app and I'm trying to intercept both:
TabShift + Tab
to perform custom actions (similar to how text editors indent/outdent items).
Right now, plain Tab works fine, but Shift + Tab never reaches my .onKeyPress(.tab) handler.
Here's what I'm currently trying:
import SwiftUI
struct ShiftTabNotIntercepted: View {
@State private var shiftKeyPressed = false
var body: some View {
Text("Hello")
.focusable()
.onKeyPress(.tab) {
print("tab pressed with shift: \(shiftKeyPressed)")
return .handled
}
.onModifierKeysChanged(mask: .shift) { old, new in
shiftKeyPressed = new.contains(.shift)
}
}
}
Behavior:
- Pressing
Tabprints:tab pressed with shift: false - Pressing
Shift + Tabdoes nothing —.onKeyPress(.tab)never fires.
I also noticed:
- if a sidebar is visible,
Shift + Tabmoves focus to the sidebar - if no sidebar is visible, it still doesn't trigger the handler
So it seems macOS is intercepting Shift + Tab for focus navigation before SwiftUI sees it.
My goal is to fully own this keyboard behavior for a custom outline/tree editor UI.
Questions:
- Is there a SwiftUI-native way to intercept
Shift + Tab? - Is
.onKeyPressfundamentally unable to capture this combination? - Do I need to drop down to AppKit (
NSViewRepresentable,keyDown, etc.) to reliably handle it?
Thanks!
Hello @fahad-sh,
I see what's going on here,
At a system level, macOS is intercepting Shift+Tab before SwiftUI ever sees it. .onKeyPress is unable to capture this.
Use NSEvent.addLocalMonitorForEvents with the following key and modifier.
if event.keyCode == 48 && event.modifierFlags.contains(.shift)
These three links cover everything needed:
struct ContentView: View {
@State private var count = 0
var body: some View {
Text(count)
.onAppear {
NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
if event.keyCode == 48 && event.modifierFlags.contains(.shift) {
count += 1
return nil
}
return event
}
}
}
}
I hope that helps!
Travis