How to add Keyboard Shortcuts to any SwiftUI App with UIKeyCommand

Overview

SwiftUI-Keyboard-Demo

This tiny project was built to show how simple it is to add keyboard shortcuts (with UIKeyCommand) to any SwiftUI app. After implementing the main parts below, you'll have the hold-down-⌘ button work in all of the views where you want to support it, listing out every keyboard shortcut available to the user. The user expects it, don't let them down!

⌘ Key Preview

Full working example for basic tab switching

If all you want to do is add add Cmd-1, Cmd-2, etc keyboard shortcuts to active different tabs, literally all you need is the following:

AppDelegate

  • Add override var keyCommands which returns an array of UIKeyCommand, and a handler to handle those using NotificationCenter
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    override var keyCommands: [UIKeyCommand]? {
        return [
            UIKeyCommand(title: "First Tab", action: #selector(handleKeyCommand(sender:)), input: "1", modifierFlags: .command, propertyList: 1),
            
            UIKeyCommand(title: "Second Tab", action: #selector(handleKeyCommand(sender:)), input: "2", modifierFlags: .command, propertyList: 2)
        ]
    }
    
    @objc func handleKeyCommand(sender: UIKeyCommand) {
        if let tabTag = sender.propertyList as? Int {
            NotificationCenter.default.post(name: .init("switchTabs"), object: tabTag)
        }
    }
}

ContentView

  • Add onReceive to handle the posted Notification and use the selection binding to jump to the correct tab
import SwiftUI

struct ContentView: View {
    @State private var selection = 1
    
    var body: some View {
        TabView(selection: $selection){
            Text("First View")
                .tabItem {
                    VStack {
                        Image("first")
                        Text("First")
                    }
            }
            .tag(1)
            
            Text("Second View")
                .tabItem {
                    VStack {
                        Image("second")
                        Text("Second")
                    }
            }
            .tag(2)
        }
        .onReceive(NotificationCenter.default.publisher(for: Notification.Name("switchTabs"))) { notification in
            if let tabTag = notification.object as? Int {
                self.selection = tabTag
            }
        }
    }
}

In the full demo, I also show how you can use an ObservableObject and state for a more complicated example, to allow for opening and closing modals, using hidden keyboard shortcuts, etc, but the basics are the same. The app will always look to the keyCommands defined in the AppDelegate, so based on the user's current view, you would simply return a different array of UIKeyCommands.

There's a more thorough walkthrough that I'll be writing up on Medium soon, but I wanted to get this out there as soon as possible as I couldn't find any solution through Google or asking some of the great SwiftUI fans on Twitter and elsewhere for a clean way to do this. I purposely provided a few different methods to help you find the best way to integrate these keyboard shortcuts in your apps, and urge you to please add them ASAP. It's just behind adding accessibility to an app in my view, especially with the amazing new Magic Keyboard for iPad Pro, you likely have a lot more users now who will be looking for keyboard shortcuts in your iPad app.

Invisible Keyboard Shortcuts

One more cool thing in here is adding "invisible" shortcuts by leaving the title of your UIKeyCommand empty, as you can see implemented for modals. Please add support for the universal methods to close any kind of modal and make the iPad world a better place, ⌘-W and Esc (which will also give you ⌘-. for free, as it sends Esc when used):

UIKeyCommand(title: "", action: #selector(handleKeyCommand(sender:)), input: UIKeyCommand.inputEscape, propertyList: "closeModal"),
UIKeyCommand(title: "", action: #selector(handleKeyCommand(sender:)), input: "W", modifierFlags: .command, propertyList: "closeModal")

Users-Will-Love-You Keyboard Shortcut

I also added in the backtick, as that is where Esc would normally be on a US keyboard, after seeing the always-awesome-and-funny Christian Selig add it into his equally awesome Apollo app for Reddit and though it was just too damn clever not use myself in my app:

UIKeyCommand(title: "Close", action: #selector(handleKeyCommand(sender:)), input: "`", propertyList: "closeModal")

Try it out in the App Store with CardPointers

Speaking of which, shameless plug ahead: if you have a credit card or you're looking for your first one(s) to help get the most cash back or points/miles to make traveling ridiculously cheap/free, or if you just want to play with a free live app in the App Store that's using these exact same keyboard shortcut techniques, grab CardPointers from the App Store, you'll be happy you did.

Contact/PRs/Issues

PRs are most welcome and I'll do my best to answer any questions; best way to reach me is through Twitter @emcro.

You might also like...
A Meetings app where the user is presented with the number of meetings created , join any them , edit the varied details also keeping the track of the history of the meetings.
A Meetings app where the user is presented with the number of meetings created , join any them , edit the varied details also keeping the track of the history of the meetings.

Meetings A Meetings app where the user is presented with the number of meetings created , join any them , edit the varied details also keeping the tra

iOS App that allows a pilot to check drone photo collection after a mapping mission for any issues
iOS App that allows a pilot to check drone photo collection after a mapping mission for any issues

DronePhotoChecker iOS App that allows a pilot to check drone photo collection after a mapping mission for any issues. Features: Connects to DJI drone

iOS app that allows you to search for any comic character and save your favorites in a list.
iOS app that allows you to search for any comic character and save your favorites in a list.

iOS App: My Comics Swift exercise ¡Hola mundo! In my Swift learning journey my mentor and I decided to do an app using the Comic Vine API. This API gi

Add a software notch to your outdated Macbook! Deceive strangers!
Add a software notch to your outdated Macbook! Deceive strangers!

The Notch Draws a persistent little cutout out at the top of the screen, on top of everything else. Purpose Each year when Apple releases a new Macboo

MapApp - You can save the location of the places you go on the map and add names and notes
MapApp - You can save the location of the places you go on the map and add names and notes

MapApp - You can save the location of the places you go on the map and add names and notes

 ReactionButton is a control that allows developers to add this functionality to their apps in an easy way.
ReactionButton is a control that allows developers to add this functionality to their apps in an easy way.

Since Facebook introduced reactions in 2016, it became a standard in several applications as a way for users to interact with content. ReactionButton is a control that allows developers to add this functionality to their apps in an easy way.

SwiftUIWordpressClient - An iOS application for any WordPress website
SwiftUIWordpressClient - An iOS application for any WordPress website

SwiftUIWordpressClient SwiftUIWordpressClient is an iOS application for any Word

Simulate any device and settings on one simulator or device.
Simulate any device and settings on one simulator or device.

SwiftUI-Simulator Enables the following settings without settings or restarting the simulator or real device. Any device screen Light/Dark mode Locale

Comments
  • Implement POC of passing KeyCommand via Preference mechanism

    Implement POC of passing KeyCommand via Preference mechanism

    So here's proof of concept of my idea to pass key commands with help of Preferences #1

    This is definitely not production ready solution, it has few drawbacks at first sight

    1. I'm not sure that wrapper around UIHostingController is optimal solution to observe preferences

    It is hard to use subclassing there, because when passing action selector down to UIKeyCommand I use action: #selector(KeyCommandResponderHostingController.handleKeyCommand) and if KeyCommandResponderHostingController is subclass, this selector will require generic constraint and I don't know Content because it is some View

    1. Right now I couldn't find best way to pass KeyCommands from ModalView looks like they are lost at some point, maybe sheet operator doesn't pass-through preferences

    2. It is not trivial to override keyCommands

    When modal is shown there's still First View and Second View commands available. One potential solution is to provide overrideKeyCommand operator that will set array, instead of appending

    So any way, I hope this will help you in some way :) Feel free to close this if needed!

    opened by asiliuk 1
  • KeyCommands via PreferenceKey

    KeyCommands via PreferenceKey

    Hey! Really cool idea and implementation!

    I wanted to ask if you considered implementing this via Preference mechanism? It looks like a really good fit for this, because it can combine all KeyCommands that are defined on each view and handle them in global scope

    I've played with that idea and your project and it is possible to do.

    1. You can create wrapper around UIHostingController
    2. Collect all KeyCommands with .onPreferenceChange on ContentView
    3. Handle all calls to commands action in that Wrapper controller

    And after that you can use helper method on Views inplace like this

    // in ContentView
    .keyCommands(
        KeyCommand(title: "First View", input: "1", modifierFlags: .command) { self.selection = 1 },
        KeyCommand(title: "Second View", input: "2", modifierFlags: .command) { self.selection = 2 }
    )
    

    if you interested I can open PR with implementation :)

    opened by asiliuk 1
  • Support for multiple scenes in iPad

    Support for multiple scenes in iPad

    Great example!

    But one problem I encountered when using NotificationCenter to handle UIKeyCommand events is the inability to differentiate different scenes.

    For example, I have the demo project opened in 2 scenes side by side, when Command + O is pressed, both scenes launch the modal. What if I only want one of the scenes respond to that UIKeyCommand? How could I achieve that?

    So far I couldn't find the right solution. I wonder if you have any solution to that?

    opened by bummoblizard 0
Owner
Emmanuel Crouvisier
Emmanuel Crouvisier
The app provides additional actions for the Shortcuts app on macOS and iOS.

Actions Supercharge your shortcuts The app provides additional actions for the Shortcuts app on macOS and iOS. Submit action idea (Submit an issue bef

Sindre Sorhus 1.2k Dec 29, 2022
An example project to demonstrate the new scripting capabilities of Shortcuts For Mac.

Scripting Shortcuts Test Project This simple project is designed to test the new scripting capabilities introduced in the Shortcuts app in macOS Monte

Alex Hay 16 Nov 17, 2022
Shortcuts support for Micro.blog

Ringgold The intial goal of this project is to provide Shortcuts support for Micro.blog. It may be expanded to include support for other Micropub impl

Maurice Parker 9 Nov 2, 2022
Add to-do List - a mobile application where you can add your to-dos and follow them

This project, is a mobile application where you can add your to-dos and follow them. You can add your to-do's.

Cem 4 Apr 1, 2022
SnackBar that responds to the keyboard and shows a message at the bottom of the screen.

DGSnackBar Requirements Installation Usage Properties DGSnackBar SnackBar that responds to the keyboard and shows a message at the bottom of the scree

donggyu 11 Aug 6, 2022
A SwiftUI wrapper around the `Add to Siri` button used to donate INIntents to the system.

AddToSiri A SwiftUI wrapper around the Add to Siri button used to donate INIntents to the system. Originally created by Reddit user u/dippnerd (Github

Florian Schweizer 5 Nov 23, 2022
SwiftUIIndexedList - Add an index bar to a SwiftUI List or ScrollView

SwiftUIIndexedList Add an index bar to a SwiftUI List or ScrollView. Getting Sta

Ciaran O'Brien 25 Oct 27, 2022
A realistic reflective shimmer to SwiftUI Views that uses device orientation. Position any View relative to device orientation to appear as if through a window or reflected by the screen.

A 3d rotation effect that uses Core Motion to allow SwiftUI views to appear projected in a specific direction and distance relative to the device in r

Ryan Lintott 235 Dec 30, 2022
iOS App to display game lists and details with the ability to add games to your favorites list and see metacritic ratings.

Game Data System - GDS Author: Heitor Silveira ([email protected]) iOS App to view games from various platforms, their description, release

Heitor Ugarte Calvet da Silveira 0 Oct 6, 2021
Cookbook app that allows you to read, add and modify your recipes.

What-s-For-Dinner-iOS-App Cookbook app that allows you to read, add and modify your recipes. Features ?? ?? View You can see recipes divided into cate

Katie Saramutina 2 May 18, 2022