惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
MyScale Blog
MyScale Blog
雷峰网
雷峰网
量子位
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
T
Tailwind CSS Blog
月光博客
月光博客
博客园 - 【当耐特】
博客园_首页
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
WordPress大学
WordPress大学
The Cloudflare Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
SegmentFault 最新的问题
人人都是产品经理
人人都是产品经理
V
V2EX
酷 壳 – CoolShell
酷 壳 – CoolShell

Peter Steinberger

OpenClaw, OpenAI and the future | Peter Steinberger Shipping at Inference-Speed | Peter Steinberger The Signature Flicker | Peter Steinberger Just Talk To It - the no-bs Way of Agentic Engineering | Peter Steinberger Claude Code Anonymous | Peter Steinberger Live Coding Session: Building Arena | Peter Steinberger My Current AI Dev Workflow | Peter Steinberger Essential Reading for Agentic Engineers - August 2025 | Peter Steinberger Just One More Prompt | Peter Steinberger Poltergeist: The Ghost That Keeps Your Builds Fresh | Peter Steinberger Don't read this Startup Slop | Peter Steinberger Essential Reading for Agentic Engineers - July 2025 | Peter Steinberger Self-Hosting AI Models After Claude's Usage Limits | Peter Steinberger Logging Privacy Shenanigans | Peter Steinberger VibeTunnel's first AI-anniversary | Peter Steinberger Making AppleScript Work in macOS CLI Tools: The Undocumented Parts | Peter Steinberger Peekaboo 2.0 – Free the CLI from its MCP shackles | Peter Steinberger Command your Claude Code Army, Reloaded | Peter Steinberger Essential Reading for Agentic Engineers | Peter Steinberger Slot Machines for Programmers: How Peter Builds Apps 20x Faster with AI | Peter Steinberger My AI Workflow for Understanding Any Codebase | Peter Steinberger stats.store: Privacy-First Sparkle Analytics | Peter Steinberger Showing Settings from macOS Menu Bar Items: A 5-Hour Journey | Peter Steinberger VibeTunnel: Turn Any Browser into Your Mac's Terminal | Peter Steinberger Vibe Meter 2.0: Calculating Claude Code Usage with Token Counting | Peter Steinberger llm.codes: Make Apple Docs AI-Readable | Peter Steinberger Automatic Observation Tracking in UIKit and AppKit: The Feature Apple Forgot to Mention | Peter Steinberger Peekaboo MCP – lightning-fast macOS screenshots for AI agents | Peter Steinberger Migrating 700+ Tests to Swift Testing: A Real-World Experience | Peter Steinberger Commanding Your Claude Code Army | Peter Steinberger
Adding Keyboard Shortcuts To UIAlertView | Peter Steinberger
Peter Steinberger · 2013-03-07 · via Peter Steinberger

I’m not even home from the more-than-excellent NSConference in Leicester, but had to hack on something super awesome that Evan Doll of Flipboard presented earlier today. They added keyboard support to UIAlertView and UIActionSheet (amongst other things) — simply to make debugging in the Simulator faster by accepting Esc and Enter keys — something that Apple should have done anyway. There’s not much value in shipping this in release builds, except better support for bluetooth keyboards. And since this hack uses private API AND accesses a struct with a memory layout that could change, I don’t recommend shipping it. If you do, make sure that you whitelist iOS versions and block this method by default on unknown future versions of iOS. I’m using it in PSPDFKit only when compiled in DEBUG mode for the Simulator.

The actual hack is mostly based on this blog post about intercepting the keyboard on iOS, and it’s not pretty. I had to modify some constants to make it work on iOS 5/6:

#define GSEVENT_TYPE 2
#define GSEVENT_FLAGS 12
#define GSEVENTKEY_KEYCODE 15
#define GSEVENT_TYPE_KEYUP 10
// http://nacho4d-nacho4d.blogspot.co.uk/2012/01/catching-keyboard-events-in-ios.html
__attribute__((constructor)) static void PSPDFKitAddKeyboardSupportForUIAlertView(void) {
    @autoreleasepool {
        // Hook into sendEvent: to get keyboard events.
        SEL sendEventSEL = NSSelectorFromString(@"pspdf_sendEvent:");
        IMP sendEventIMP = imp_implementationWithBlock(^(id _self, UIEvent *event) {
            objc_msgSend(_self, sendEventSEL, event); // call original implementation.

            SEL gsEventSEL = NSSelectorFromString([NSString stringWithFormat:@"%@%@Event", @"_", @"gs"]);
            if ([event respondsToSelector:gsEventSEL]) {
                // Key events come in form of UIInternalEvents.
                // They contain a GSEvent object which contains a GSEventRecord among other things.
                int *eventMem = (int *)[event performSelector:gsEventSEL];
                if (eventMem) {
                    if (eventMem[GSEVENT_TYPE] == GSEVENT_TYPE_KEYUP) {
                        UniChar *keycode = (UniChar *)&(eventMem[GSEVENTKEY_KEYCODE]);
                        int eventFlags = eventMem[GSEVENT_FLAGS];
                        //NSLog(@"Pressed %d", *keycode);
                        [[NSNotificationCenter defaultCenter] postNotificationName:@"PSPDFKeyboardEventNotification" object:nil userInfo: @{@"keycode" : @(*keycode), @"eventFlags" : @(eventFlags)}];
                    }
                }
            }
        });
        PSPDFReplaceMethod(UIApplication.class, @selector(sendEvent:), sendEventSEL, sendEventIMP);

        // Add keyboard handler for UIAlertView.
        SEL didMoveToWindowSEL = NSSelectorFromString(@"pspdf_didMoveToWindow");
        IMP didMoveToWindowIMP = imp_implementationWithBlock(^(UIAlertView *_self, UIEvent *event) {
            objc_msgSend(_self, didMoveToWindowSEL, event); // call original implementation.

            static char kPSPDFKeyboardEventToken;
            if (_self.window) {
                id observerToken = [[NSNotificationCenter defaultCenter] addObserverForName:@"PSPDFKeyboardEventNotification" object:nil queue:nil usingBlock:^(NSNotification *notification) {
                    NSUInteger keyCode = [notification.userInfo[@"keycode"] integerValue];
                    if (keyCode == 41) /* ESC */ {
                        [_self dismissWithClickedButtonIndex:_self.cancelButtonIndex animated:YES];
                    }else if (keyCode == 40) /* ENTER */ {
                        [_self dismissWithClickedButtonIndex:_self.numberOfButtons-1 animated:YES];
                    }
                }];
                objc_setAssociatedObject(_self, &kPSPDFKeyboardEventToken, observerToken, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            }else {
                id observerToken = objc_getAssociatedObject(_self, &kPSPDFKeyboardEventToken);
                if (observerToken) [[NSNotificationCenter defaultCenter] removeObserver:observerToken];
            }
        });
        PSPDFReplaceMethod(UIAlertView.class, @selector(didMoveToWindow), didMoveToWindowSEL, didMoveToWindowIMP);
    }
}

You will also need some swizzling helpers. Here’s what I use:

// http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html
static void PSPDFSwizzleMethod(Class c, SEL orig, SEL new) {
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);
    if (class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
        class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    }else {
        method_exchangeImplementations(origMethod, newMethod);
    }
}

void PSPDFReplaceMethod(Class c, SEL orig, SEL newSel, IMP impl) {
    Method method = class_getInstanceMethod(c, orig);
    if (!class_addMethod(c, newSel, impl, method_getTypeEncoding(method))) {
        PSPDFLogError(@"Failed to add method: %@ on %@", NSStringFromSelector(newSel), c);
    }else PSPDFSwizzleMethod(c, orig, newSel);
}