You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需NSEvent,通过CoreServices将KeyCode映射为当前键盘布局对应Unicode字符

问题背景

我正在某应用中实现键盘热图功能,为保证热图与键盘布局无关,我将KeyCode与对应字符分开存储,热图将在以下视图集合上渲染:
键盘示意图
我的需求是将KeyCode(例如标准美式Qwerty键盘上对应G的KeyCode为5)关联到正确的Unicode字符,优先渲染用户当前正在使用的键盘布局对应的字符,无对应字符时可留白。

实现方案

完全不需要逆向解析AppleKeyboardLayouts-L.dat文件,直接使用macOS原生提供的CoreServices + TextInputMenu框架API即可实现需求,核心步骤如下:

  • 获取当前活跃的键盘输入源:调用TISCopyCurrentKeyboardInputSource()获取当前用户正在使用的键盘输入源对象TISInputSourceRef
  • 提取键盘布局数据:调用TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData)获取对应键盘布局的二进制数据,数据结构就是标准的UCKeyboardLayout类型
  • 执行KeyCode到Unicode的转换:调用CoreServices提供的UCKeyTranslate方法完成映射,核心参数说明:
    • 第一个参数传入上述获取到的UCKeyboardLayout指针
    • 第二个参数传入你要转换的硬件KeyCode
    • 第三个参数传入修饰键状态,无修饰键传0即可,需要展示Shift/Option等组合键字符时传入对应修饰位
    • 第四个参数传入当前系统的键盘类型,可通过LMGetKbdType()获取
    • 第五个参数传入kUCKeyTranslateNoDeadKeysMask,避免死键(例如法语布局的重音键)返回组合占位符,适配热图静态展示的场景
    • 最后输出转换得到的Unicode字符数组
  • 边界处理:功能键(F1-F12、修饰键、媒体键等)转换后无有效可打印字符,直接留白即可

核心代码示例(Objective-C)

#import <CoreServices/CoreServices.h>
#import <Carbon/Carbon.h>

NSString *getStringForKeyCode(uint16_t keyCode, UInt32 modifierFlags) {
    TISInputSourceRef inputSource = TISCopyCurrentKeyboardInputSource();
    CFDataRef layoutData = TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData);
    const UCKeyboardLayout *keyLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
    
    UInt32 deadKeyState = 0;
    UniChar chars[4];
    UniCharCount realLength;
    
    OSStatus status = UCKeyTranslate(keyLayout,
                                     keyCode,
                                     kUCKeyActionDisplay,
                                     modifierFlags >> 16,
                                     LMGetKbdType(),
                                     kUCKeyTranslateNoDeadKeysMask,
                                     &deadKeyState,
                                     4,
                                     &realLength,
                                     chars);
    CFRelease(inputSource);
    
    if (status == noErr && realLength > 0) {
        return [[NSString alloc] initWithCharacters:chars length:realLength];
    }
    return nil;
}

调用时如果要获取普通无修饰的字符,直接传modifierFlags = 0即可,返回nil时直接在热图上留白即可。

内容的提问来源于stack exchange,提问作者Antwan van Houdt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 15:36:03