SwiftUI Tips: Handling the Esc Key

Kyaw Zay Ya Lin Tun
2 min readDec 21, 2024

--

Recently, I tried to close my macOS app using the Esc key. While there are plenty of tutorials explaining how to achieve this, I ran into a few challenges along the way. So, I thought I’d document my findings here.

NOTE: I’m not an expert in macOS development, so feel free to correct me if I’ve made any incorrect assumptions.

Basic Key Press Handling

Let’s start with how to listen for keyboard shortcuts in SwiftUI. There’s a modifier called onKeyPress with several overloads. However, since I wanted to listen specifically for the Esc key, I opted to use the onExitCommand modifier instead. Why? Well, it’s easier to read, in my opinion.

.onExitCommand {
// Handle ESC key press
}

Making Views Respond to Keyboard Events

At this point, I assumed everything was set up to handle key presses. Not quite! To listen to any key press, a view needs to be focusable. That part is straightforward: just add the focusable modifier before applying the key press listener. (Yes, the order matters!)

Now, I launched my app. It opened in the foreground, and I was ready to test. But when I pressed the Esc key, my Mac played a “ding” sound, which meant the app’s view wasn’t focused. Strange, right? The app was interactive, yet the view didn’t have focus. I had to manually click the window or view to give it focus and see the focus ring appear. Only then did the key press listener work. Not cool.

I wanted my app’s window to automatically focus as soon as it appeared on the screen. That’s where @FocusState came to the rescue. Here’s how I solved it:

  1. I created a simple @FocusState variable, like @FocusState private var isFocused: Bool.
  2. In the onAppear modifier, I set this variable to true. Similarly, I set it to false in onDisappear.
  3. I connected the @FocusState variable to the view using the focused($isFocused) modifier.

With this setup, my app’s window automatically gained focus when it appeared on screen, and it correctly listened for the Esc key press.

One last thing — I didn’t want the focus ring to appear in my app. It looked odd in this case. Thankfully, I could turn it off using the focusEffectDisabled() modifier.

Here’s a snippet of my final code:

struct ContentView: View {
@FocusState private var isFocus: Bool

var body: some View {
MainContentView()
.focusable()
.focused($isFocus)
.onExitCommand {
NSApplication.shared.terminate(nil)
}
.onAppear {
isFocus = true
}
.onDisappear {
isFocus = false
}
}
}

Now, the app’s window gains focus automatically, listens for the Esc key press, and doesn’t display the focus ring. Problem solved! 🎉

--

--

Kyaw Zay Ya Lin Tun
Kyaw Zay Ya Lin Tun

Written by Kyaw Zay Ya Lin Tun

Lead iOS Dev @CodigoApps • Programming Mentor • Swift enthusiast • Community Builder • Organising CocoaHeads Myanmar 🇲🇲

No responses yet