Controlling keyboard events with keys and phases

Controlling keyboard events with keys and phases

Learn how to respond to pressed keys and phases in a hardware keyboard in a SwiftUI app.

The onKeyPress(_:action:) method is present in several variations, providing complete control over triggering actions. These variations allow the developer to specify exactly how and when actions should be executed in response to key presses.

The behavior can be customized by specifying:

  1. particular keys being pressed;
  2. the phase of a key press;
  3. a combination of both keys and their phases;
  4. the characters produced by the pressed keys.
The following code snippets have been tested using an iPad magic keyboard. Remember that the onKeyPress modifier works with physical keyboards.

Specifying the key pressed with onKeyPress(_:action:)

It triggers a specific action when a particular key is pressed on a hardware keyboard, provided the view is in focus.

MyView()
    .onKeyPress(
        // 1. The key that triggers the action once it is pressed
        KeyEquivalent("a"), 
        // 2. The action to perform
        action: {
              print("You have just pressed the 'a' key ")
              // 3. Returning value
              return .handled
        }
    )

The method needs two parameters:

  1. A key, a KeyEquivalent type, consisting of any letter, punctuation, or function key being pressed on the hardware keyboard;
  2. The action that will be performed once one or more keys have been pressed.

The following is an example of the modifier being implemented on a view:

import SwiftUI

struct ContentView: View {

    @FocusState var isFocused: Bool?
    @State var text: String = "Hello, world!"
    @State var input: String = ""
        
    var body: some View {
        VStack {
            Image(systemName: "keyboard")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Press the 'a' key to change the text below")
                .foregroundStyle(.secondary)
             Text(text)
                .foregroundStyle(.secondary)
                
                Divider()
            
            TextField("Press the keys", text: $input)
                .frame(width: 200)
                .padding(.horizontal)
                
                .focusable()
                .focused($isFocused, equals: true)
                
                // The method
                .onKeyPress(KeyEquivalent("a"), action: {
                    text = "You have just pressed the 'a' key"
                    return .handled
                })
        }
        .padding()
        
        .onAppear(perform: {
            isFocused = true
        })
    }
}
0:00
/0:11

Specifying the phase with onKeyPress(phases:action:)

It triggers a specific action when any key is pressed on a hardware keyboard, based on the phase of the KeyPress, provided the view is in focus.

.onKeyPress(
    // 1. The phase that triggers the action
    phases: .up, 
    // 2. The action to perform
    action: {
        // 3. The key being pressed
        pressedKey in
        text = "You have just pressed and released the '\(pressedKey.key.character)' key"
        // 4. Returning value
        return .handled
    }
)

This method works as follows:

  1. It takes a phases parameter, a KeyPress.Phases collection type that describes the different phases of a key-press event, which can be set. By default, the value is set to [.down, .repeat].
    1. down refers to when the user presses down on a key.
    2. up refers to when the user releases a key.
    3. repeat refers to when the user holds a key down generating a sequence of repeating events.
    4. all matches all key press phases.
  2. It takes the action to be performed once one or more keys have been pressed.
  3. The information regarding the pressed key is provided to the action as a KeyEquivalent object.
  4. The returning value to handle the action.
0:00
/0:09

Specifying one key and the phase with onKeyPress(_:phases:action:)

It triggers an action when a specific key is pressed on a hardware keyboard, based on a specificphase of the KeyPress, provided the view is in focus.

// The method
.onKeyPress(
    // 1. The key to be pressed
    KeyEquivalent("a"),
    // 2. The phase that triggers the action
    phases: [.down, .up], 
    // 3. The action to perform
    action: { pressedKey in
        if pressedKey.phase == .down {
            text = "You are pressing the '\(pressedKey.key.character)' key"
        } else if pressedKey.phase == .up {
            text = "You have just released the '\(pressedKey.key.character)' key"
        }
        // 4. Returning value
        return .handled
    }
)

This method takes 3 parameters:

  1. The key to be pressed to trigger the action;
  2. The phases when triggering the action - that can be of value downrepeat, and up;
  3. The action to be performed.
0:00
/0:10

Specifying a set of keys and the phase with onKeyPress(keys:phases:action:)

It triggers an action when specific keys are pressed on a hardware keyboard, based on a specific phase of the KeyPress, provided the view is in focus.

// The method
.onKeyPress(
    // 1. The key to be pressed
    keys: [KeyEquivalent("o"), KeyEquivalent("k")],
    // 2. The phase that triggers the action
    phases: .up, 
    // 3. The action to perform
    action: { _ in
        text = "You've just pressed and released the \(pressedKey.key.character) keys"			
        // 4. Returning value
        return .handled
    }
)

This method takes 3 parameters:

  1. The keys, a collection of the KeyEquivalent type to be pressed to trigger the action;
  2. The phases when triggering the action - that can be of value downrepeat, and up , set as [.down, .repeat] by default.
  3. The action to be performed.
0:00
/0:13

Specifying characters with onKeyPress(characters:phases:action:)

It triggers an action when specific characters are generated by keys being pressed on a hardware keyboard, at a givenphase of the KeyPress, provided the view is in focus.

// The method
.onKeyPress(
    // 1. The characters to be generated
    characters: .letters,
    // 2. The phase that triggers the action
    phases: .up, 
    // 3. The action to perform
    action: { _ in
        text = "You've just pressed and released the \(pressedKey.key.character) key"
    // 4. Returning value
        return .handled
    }
)

This method takes 3 parameters:

  1. The characters, of type CharacterSet, representing the set of characters generated by the pressed key;
  2. The phases when triggering the action - that can be of value downrepeat, and up , set as [.down, .repeat] by default.;
  3. The action to be performed.
0:00
/0:15