NSResponder actions in TableFlip to move the selection around. Naturally, neither a standard
NSTableView nor a custom
NSResponder implement default behavior for (most of) the methods I need to support arrow key movement, tab movement, and the usual shortcuts, like Cmd-Left to jump to the first cell in the current row. Or Alt + arrow keys to insert a row or column next to the selection. So I implemented this in a custom
Curiously, the app beeped when I hit Alt-Up or Alt-Down, the actions to insert a row above or below the current selection. I implemented
moveToBeginningOfParagraph(_:) to consume these events but still the system “input error” sound was audible while the key combo itself performed the action just fine.
How do you find out the source of problems like this? Well, this is how I dug in.
- The custom
keyDown(_:)with a call to
nextResponder?.interpretKeyEvents(_:)– but not delegating to
super, thus consuming the event, to disable standard
- Note that
keyUp(_:)would in general produce a similar effect but probably beep for every shortcut;
keyDown(_:)has a special meaning in all this, so make sure to override this method.
- The next responder implements
moveToBeginningOfParagraph(_:). This method is called just fine thanks to
interpretKeyEvents(_:)being called. This method maps common shortcuts to
If you have a custom
NSView subclass, you can call
self.interpretKeyEvents(_:), too, instead of pushing it to the next responder; it’s just that I want to bypass the table view in this particular case.
NSTableView.interpretKeyEvents results in unwanted behavior.
This is where the beep happened even though the Alt-Up/Alt-Down
NSResponder methods were present. The input error sound usually plays when you don’t implement a shortcut anywhere in the responder chain.
According to lore and the docs,
performKeyEquivalent(with:) -> Bool should be useful: you return
true to indicate that the receiver took care of the event selector passed to it. In theory, this should prevent the beep sound even when you don’t implement the
NSResponder method – but that didn’t work at all for me.
These are the steps I found useful to hunt for the source:
- Add a symbolic breakpoint in Xcode for symbol “NSBeep” in module “AppKit”. When you hit enter or run the program for the first time, the breakpoint should obtain a child item; that indicates the symbol is recognized. Run the program, hit the key combo, and Xcode should stop execution. Look at the stack trace to see from where the key interpretation reaches nirvana. – I found out that shortcut execution indeed reaches
interpretKeyEvents(_:)but seemingly bypasses the rest of the responder chain which should handle the expected
moveToBeginningOfParagraph(_:)call. (At least when it beeps. Since the action is performed by the program, I know the method is invoked.)
doCommandBySelector(_:)in your target responder. Insert a manual breakpoint. Look at the value of the
selectorparameter for every call.
The first step revealed that some methods were never called when the beep happened. The second step told me which selector was actually invoked for a single hit of Alt-Up:
Alt-Down produces a similar combo:
Once I added an empty implementation of
moveForward(_:), the beeps went away.
NSResponder debugging isn’t much fun most of the time because of all the implicit behavior. Then again, you get a lot of useful stuff for free. Sheesh. At least now I know that
doCommandBySelector(_:) is a great opportunity to see what’s done behind the scenes because it’s a bottleneck every call has to pass.
Browse the blog archive