HardwareKeyboardInputView.swift (5499B)
1 import SwiftUI 2 import UIKit 3 4 struct HardwareKeyboardEvent { 5 let keyCode: UIKeyboardHIDUsage 6 let charactersIgnoringModifiers: String 7 let modifierFlags: UIKeyModifierFlags 8 } 9 10 struct HardwareKeyboardInputView: UIViewRepresentable { 11 var onPress: (HardwareKeyboardEvent) -> Bool 12 13 func makeUIView(context: Context) -> KeyCaptureView { 14 let view = KeyCaptureView() 15 view.onPress = onPress 16 return view 17 } 18 19 func updateUIView(_ uiView: KeyCaptureView, context: Context) { 20 uiView.onPress = onPress 21 uiView.ensureFirstResponder() 22 } 23 24 final class KeyCaptureView: UIView { 25 var onPress: ((HardwareKeyboardEvent) -> Bool)? 26 27 override var canBecomeFirstResponder: Bool { true } 28 29 override var keyCommands: [UIKeyCommand]? { 30 let letters = "abcdefghijklmnopqrstuvwxyz".map { 31 UIKeyCommand( 32 input: String($0), 33 modifierFlags: [], 34 action: #selector(handleKeyCommand(_:)) 35 ) 36 } 37 38 return letters + [ 39 UIKeyCommand(input: UIKeyCommand.inputLeftArrow, modifierFlags: [], action: #selector(handleKeyCommand(_:))), 40 UIKeyCommand(input: UIKeyCommand.inputRightArrow, modifierFlags: [], action: #selector(handleKeyCommand(_:))), 41 UIKeyCommand(input: UIKeyCommand.inputLeftArrow, modifierFlags: .command, action: #selector(handleKeyCommand(_:))), 42 UIKeyCommand(input: UIKeyCommand.inputRightArrow, modifierFlags: .command, action: #selector(handleKeyCommand(_:))), 43 UIKeyCommand(input: UIKeyCommand.inputUpArrow, modifierFlags: [], action: #selector(handleKeyCommand(_:))), 44 UIKeyCommand(input: UIKeyCommand.inputDownArrow, modifierFlags: [], action: #selector(handleKeyCommand(_:))), 45 UIKeyCommand(input: "\u{8}", modifierFlags: [], action: #selector(handleKeyCommand(_:))), 46 UIKeyCommand(input: "\u{7F}", modifierFlags: [], action: #selector(handleKeyCommand(_:))), 47 UIKeyCommand(input: "\t", modifierFlags: [], action: #selector(handleKeyCommand(_:))), 48 UIKeyCommand(input: "\t", modifierFlags: .shift, action: #selector(handleKeyCommand(_:))), 49 UIKeyCommand(input: " ", modifierFlags: [], action: #selector(handleKeyCommand(_:))), 50 UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(handleKeyCommand(_:))), 51 UIKeyCommand(input: UIKeyCommand.inputEscape, modifierFlags: [], action: #selector(handleKeyCommand(_:))) 52 ] 53 } 54 55 override func didMoveToWindow() { 56 super.didMoveToWindow() 57 ensureFirstResponder() 58 } 59 60 func ensureFirstResponder() { 61 guard window != nil, !isFirstResponder else { return } 62 Task { @MainActor in 63 self.becomeFirstResponder() 64 } 65 } 66 67 override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) { 68 var unhandled: [UIPress] = [] 69 70 for press in presses { 71 guard let key = press.key else { 72 unhandled.append(press) 73 continue 74 } 75 76 let event = HardwareKeyboardEvent( 77 keyCode: key.keyCode, 78 charactersIgnoringModifiers: key.charactersIgnoringModifiers, 79 modifierFlags: key.modifierFlags 80 ) 81 82 if onPress?(event) != true { 83 unhandled.append(press) 84 } 85 } 86 87 if !unhandled.isEmpty { 88 super.pressesBegan(Set(unhandled), with: event) 89 } 90 } 91 92 @objc private func handleKeyCommand(_ command: UIKeyCommand) { 93 guard let event = HardwareKeyboardEvent( 94 characters: command.input ?? "", 95 modifierFlags: command.modifierFlags 96 ) else { return } 97 _ = onPress?(event) 98 } 99 } 100 } 101 102 private extension HardwareKeyboardEvent { 103 init?(characters: String, modifierFlags: UIKeyModifierFlags) { 104 let keyCode: UIKeyboardHIDUsage 105 106 switch characters { 107 case UIKeyCommand.inputLeftArrow: 108 keyCode = .keyboardLeftArrow 109 case UIKeyCommand.inputRightArrow: 110 keyCode = .keyboardRightArrow 111 case UIKeyCommand.inputUpArrow: 112 keyCode = .keyboardUpArrow 113 case UIKeyCommand.inputDownArrow: 114 keyCode = .keyboardDownArrow 115 case UIKeyCommand.inputEscape: 116 keyCode = .keyboardEscape 117 case "\u{8}": 118 keyCode = .keyboardDeleteOrBackspace 119 case "\u{7F}": 120 keyCode = .keyboardDeleteForward 121 case "\t": 122 keyCode = .keyboardTab 123 case " ": 124 keyCode = .keyboardSpacebar 125 case "\r": 126 keyCode = .keyboardReturnOrEnter 127 case "a"..."z", "A"..."Z": 128 guard let scalar = characters.uppercased().unicodeScalars.first, 129 let code = UIKeyboardHIDUsage(rawValue: Int(scalar.value - 65) + UIKeyboardHIDUsage.keyboardA.rawValue) else { 130 return nil 131 } 132 keyCode = code 133 default: 134 return nil 135 } 136 137 self.init( 138 keyCode: keyCode, 139 charactersIgnoringModifiers: characters, 140 modifierFlags: modifierFlags 141 ) 142 } 143 }