如何在无框架的原生Swift(MacOS命令行环境)中获取键盘输入?
Hey there! I totally get where you're coming from—building a native Swift CLI Pong game without any UI frameworks, and needing to catch individual keystrokes (like detecting when "A" is pressed and outputting something like "A pressed") can feel tricky when most resources focus on SwiftUI or Cocoa. Let's fix that with a POSIX-based approach that works directly in the macOS command line, no extra frameworks required.
The Problem with Default CLI Input
By default, macOS terminals use line buffering—meaning your program won't receive any input until you hit Enter. We need to tweak terminal settings to disable line buffering and echo (so the key you press doesn't show up automatically in the terminal), then read characters one at a time.
Working Swift Implementation
Here's a complete, self-contained example that does exactly what you need:
import Foundation // Store the original terminal settings to restore later var originalTerminalSettings = termios() func configureTerminalForRawInput() { // Grab the current terminal attributes tcgetattr(STDIN_FILENO, &originalTerminalSettings) var modifiedSettings = originalTerminalSettings // Disable line buffering (ICANON) and input echo (ECHO) modifiedSettings.c_lflag &= ~(UInt(ICANON) | UInt(ECHO)) // Apply the new settings immediately tcsetattr(STDIN_FILENO, TCSANOW, &modifiedSettings) } func restoreTerminalSettings() { // Put the terminal back to its original state tcsetattr(STDIN_FILENO, TCSANOW, &originalTerminalSettings) } // Make sure we restore terminal settings when the program exits atexit(restoreTerminalSettings) // Set up the terminal for raw input configureTerminalForRawInput() print("Press any key (type 'q' to quit)...") // Main loop to read keystrokes while true { let inputChar = getchar() guard inputChar != EOF else { break } // Convert the raw C char to a Unicode scalar for easy handling if let key = UnicodeScalar(UInt32(inputChar)) { print("\(key) pressed") // Exit condition: press 'q' to quit if key == "q" { print("Exiting game...") break } } }
How This Works
Let's break down the key parts:
configureTerminalForRawInput(): Uses POSIXtcgetattrandtcsetattrfunctions to modify terminal behavior. DisablingICANONturns off line buffering (so we get keys immediately), and disablingECHOstops the terminal from printing the key you press.restoreTerminalSettings(): Critical! If we don't reset the terminal when the program exits, your terminal will stay in raw mode (no echo, no line buffering) which is annoying. We useatexit()to ensure this runs automatically when the program quits.- Main Loop:
getchar()reads a single character from standard input. We convert it to aUnicodeScalarto easily check which key was pressed, then print your desired message.
Compiling & Running
Just save this code to a file (e.g., KeyReader.swift) then compile and run it with:
swiftc KeyReader.swift ./KeyReader
Bonus: Handling Special Keys (Optional)
If you later want to handle arrow keys, function keys, etc., note that these send multi-character escape sequences (e.g., the up arrow sends \u{1B}[A). You'd need to add logic to read and parse these sequences, but for basic letter keys like "A", the code above works perfectly.
内容的提问来源于stack exchange,提问作者Victor Morel

