How can I intercept Shift+Tab in SwiftUI on macOS?

Hi everyone,

I'm building a macOS SwiftUI app and I'm trying to intercept both:

  • Tab
  • Shift + 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 Tab prints:
    tab pressed with shift: false
    
  • Pressing Shift + Tab does nothing — .onKeyPress(.tab) never fires.

I also noticed:

  • if a sidebar is visible, Shift + Tab moves 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:

  1. Is there a SwiftUI-native way to intercept Shift + Tab?
  2. Is .onKeyPress fundamentally unable to capture this combination?
  3. Do I need to drop down to AppKit (NSViewRepresentable, keyDown, etc.) to reliably handle it?

Thanks!

Answered by DTS Engineer in 886982022

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

Accepted Answer

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

How can I intercept Shift+Tab in SwiftUI on macOS?
 
 
Q