3

I have moved most of the core functionality of my non-document based macOS app to a custom, embedded framework.

The app code has a standard main storyboard with an initial window, and the window has a "window content" relationship/segue into a storyboard reference pointing to a storyboard inside the embedded framework. Therein lies a custom NSViewController subclass and a custom NSView subclass.

I want to group all the input event handling code inside the framework, which means implementing mouseDown(with:) on the custom NSView subclass, and --lo and behold-- it gets called when I click inside the app window. So far, so good.

Next, I implemented keyDown(with:) to similarly handle keyboard input. However, at runtime, it does not get called and instead, I hear the annoying beep (NSBeep).

I tried implementing keyDown(with:) on the view controller instead, but it's all the same.

Finally, I tried implementing the key handler on my NSWindowController subclass instead, and that does work.

So I could get around this by forwarding the event like so:

class WindowController: NSWindowController {

    override func keyDown(with event: NSEvent) {
        contentViewController?.view.keyDown(with: event)
    }
}

, but it is very inelegant. I would prefer to not pollute the app code with input logic.

This doesn't seem to have anything to do with embedding frameworks, however. I put together a minimal project from the 'Cocoa App' template and confirmed that indeed keyDown(with:) only gets called if implemented on the window controller code, but not on the view or view controller side.

How can I get keyDown(with:) to be called on the view or view controller (not the window or window controller) in a storyboard-based app? (so I can move it from the main app to my embedded framework).


Edit: The question has been marked as duplicate. I tried the solutions pointed in answers to the other question (namely, override acceptsFirstResponder to return true). This solves the problem in my minimal demo project, but when I tried it on my full app, it still does not work (I did see that question and did try to override acceptsFirstResponder in my app before posting this question).

I will now try to modify my minimal poeject to see if I can reporduce the issue in the main app.


Edit 2: I have refactored the minimal project to:

  1. Send the view controller to a separate storyboard,
  2. Send the view controller's storyboard and represented classes (custom view, custom view controller) to a separate, embedded framework.

Now the basic setup mirrors that of my app in all that seems to matter, but still can not reproduce the issue in the minimal project. I will investiate further...


Edit 3: I haven't been able to reproduce the issue on the minimal project. On my app's custom view, I implemented:

public override var acceptsFirstResponder: Bool {
    return true
}

public override func performKeyEquivalent(with event: NSEvent) -> Bool {
    let retVal = super.performKeyEquivalent(with: event)
    return retVal
}
  1. On startup, acceptsFirstResponder is called twice.
  2. When hitting any key, performKeyEquivalent(with:) is called twice, too. Inspectig the intermediate variable retVal above reveals that the super class's implementation always returns false. After returning from this method, NSBeep() is called and keyDown(with:) isn't.
  3. If instead of super.performKeyEquivalent(with:) I force-return true, I can avert the call to NSBeep() (but keyDown(with:) is still not called...)

Edit 4 (Final):

Out of desperation, I cleared the "Custom Class" field of the window controller's Identity Inspector in my app's main storyboard (to the default NSWindowController).

Suddenly, my custom view's keyDown(with:) starts getting called.

I reinstated the custom class to confirm.

It still works.

I clean the build folder and try again.

It still works.

Now I can no longer reproduce the issue even on my main app. I really don't know what to say...

Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
  • Keydown events are sent tot the first responder. See [The Path of Key Events](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html#//apple_ref/doc/uid/10000060i-CH3-SW10) – Willeke Apr 15 '19 at 07:46
  • 1
    Possible duplicate of [Swift - Capture keydown from NSViewController](https://stackoverflow.com/questions/32446978/swift-capture-keydown-from-nsviewcontroller) – Willeke Apr 15 '19 at 07:48
  • Possible duplicate of [Swift NSViewController responds to mouseDown event, but not keyDown event](https://stackoverflow.com/questions/30723756/swift-nsviewcontroller-responds-to-mousedown-event-but-not-keydown-event) – Willeke Apr 15 '19 at 07:50
  • Possible duplicate of [How to handle keyboard events in subclass of NSViewController?](https://stackoverflow.com/questions/8148714/how-to-handle-keyboard-events-in-subclass-of-nsviewcontroller) – Willeke Apr 15 '19 at 07:50
  • @Willeke see my edits. – Nicolas Miari Apr 15 '19 at 08:17

0 Answers0