Why doesn't the keyUp event work in Swift?

I've trying to use the keyUp event in swift so then I can detect when the user presses a key and when the user releases the key.


I have the following code:


override func keyUp(event: NSEvent) {
        print("foo")
}


So shouldn't, when the guy is pressed and released, `foo` be printed to output log?


I can do this with the keyDown event:


override func keyDown(event: NSEvent) {
    print("bar")
}


and it works fine, just not for keyUp.


Is there something I'm doing wrong?

Have you tried overriding keyUp anywhere else to see if the problem just happens there?


Also, you don't have any local event monitors set up, do you? They might be accidentally intercepting and absorbing your key-up events. (If you don't know what I'm talking about, don't worry—there won't be any to worry about unless you set them up yourself.)


You might also try using a "tracking loop" your keyDown method by inserting a call to the window's nextEventMatchingMask(), looking for key-up events, and then responding to those. This link includes a discussion of tracking loops: (I know it's for mouse events, but the concepts are still valid)

https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/HandlingMouseEvents.html#//apple_ref/doc/uid/10000060i-CH6-SW18

No, I haven't tried overiding it anywhere so I will try that.


I didn't know what you were talking about so I assume I don't have any local event monitors set up 🙂


I'm not sure how to add the tracking loop. I'm afraid the examples were in Objective-C and I don't know that 😟


Could this be a bug in swift as I also asked on Stackoverflow here and someone said they were having the same issue where keyDown wouldn't work.

So I made a new Xcode project to test it out but now whenever I press a key it just makes the bong sound. Any idea why that might happen because I can't test if it happens on a brand new project with no other code at the moment becasue of that.


I should add that I'm using Xcode beta 4 version is it matters.

So I made a new Xcode project to test it out but now whenever I press a key it just makes the bong sound. Any idea why that might happen…

I'm not sure what's going on in your case but I tried this with a simple test project and it worked as expected. Here's what I did:

  1. I created a Swift project from the Cocoa Application template.

  2. I created a test view class as shown below.

    import Cocoa
    
    
    class TestView: NSView {
    
    
    
    override var acceptsFirstResponder: Bool {
        get {
            return true
        }
    }
    
    
    override func keyDown(theEvent: NSEvent) {
        NSLog("down")
        self.acceptsFirstResponder
        super.keyDown(theEvent)
    }
    
    
    override func keyUp(theEvent: NSEvent) {
        NSLog("up")
        super.keyUp(theEvent)
    }
    
    
    }
  3. I added an instance of this class to my nib and wired up a reference to it from my app delegate.

  4. In my app delegate I called

    -becomeFirstResponder
    on it.
  5. I ran the app and pressed keys. Both "down" and "up" were printed.

    2015-08-03 09:29:32.480 xxy[90211:8432000] down
    2015-08-03 09:29:32.578 xxy[90211:8432000] up
    2015-08-03 09:29:32.914 xxy[90211:8432000] down
    2015-08-03 09:29:32.994 xxy[90211:8432000] up

    It also beeped, but that's because I'm not handling the key events but rather passing them on to super.

The code worked the same on both Xcode 6.4 and Xcode 7.0b4, both running on 10.10.4.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hello eskimo 🙂


I did the following:


class MainViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func keyDown(event: NSEvent) {
        if (event.keyCode == 1){
             print("down")
        }
    }

    override func keyUp(event: NSEvent) {
          if (event.keyCode == 1){
             print("up")
        }
    }
}


I put that in my view controller. Was that wrong to do? Because when I do it the down arrow key works but the up arrow key doesn't. But then if I do exactly the same thing in a new projet it doesn't work.

Did you notice that eskimo included the following method in his code?

override var acceptsFirstResponder: Bool { 
    get { 
        return true 
    } 
}


Unless you include this one, then your view cannot respond to key events.

I added that and it made no differende. keyDown still works and keyUp still doesn't.


My ViewController looks like this:


class MainViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override var representedObject: AnyObject? {
        didSet {
        }
    }
  
    override var acceptsFirstResponder: Bool {
        get {
            return true
        }
    }
  
    override func keyDown(theEvent: NSEvent) {
        NSLog("down")
        self.acceptsFirstResponder
        super.keyDown(theEvent)
    }
  
    override func keyUp(theEvent: NSEvent) {
        NSLog("up")
        super.keyUp(theEvent)
    }
}

down is printer to the log when I press a button but not up.

How is your view controller getting into the responder chain? I tried two ways of doing this, one of which replicated your results:

  • If I create a custom view, having accept first responder, and then make it the first responder, my view controller gets both key up and key down events.

  • If I add a text field to my view and click in that, the view controller gets key down events but not key up events.

At this point it's clear that this is definitely not related to Swift but rather something very specific to AppKit. I don't know AppKit (or any UI framework, for that matter) well enough to help you with anything beyond the basics. I've moved this thread to App Frameworks > Cocoa; hopefully someone more knowledgeable about this stuff will chime in over tehre.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Ok - thank you for your help.


I have a window contoller which is the initial controller and then this view is displayed first by the window controller.


On the view I have 2 web views, 5 custom views and one embeded view if that matters 🙂

As it turns out, I'm pretty good with AppKit. So which view are you having problems with?


I think that part of your problem may be that you're trying to handle events in your view controller, not your view. The view controller is supposed to manage the views and handle synchronization between the data storage and how it's displayed (a design pattern known as model-view-controller), not do the job of the views. Have you learned about the responder chain, a key mechanism in this interaction? Check it out in the documentation.

The view I'm having trouble with is the main one with everythign embededd into it.


I thought I coudl put that code in there then when they pressed a key it would call either the keyUp or keyDown function. When that function was called it woudl update some variabels then call one of the custom views (depending on the key that was pressed) to repaint.

Try clicking on different subviews to highlight them, and see if that affects the problem. The key events will actually go first to whatever's highlighted (aka the first responder); only if the first responder ignores the event will it move up the chain to the superview. It seems that a subview is allowing the key-down events to move up the chain but is blocking the key-up events. Try adding a custom view to your superview with code like this to verify if that's the problem:

class TestView : NSView {


override var acceptsFirstResponder: Bool { 
        get { 
            return true 
        } 
    } 

     override func keyDown(theEvent: NSEvent) {
          self.nextResponder.keyDown(theEvent);
     }

     override func keyUp(theEvent: NSEvent) {
          self.nextResponder.keyUp(theEvent);
     }
}


This code will manually pass the events up along the responder chain (see lines 10 and 14). Add it to your app, click it in the window to highlight it, then try pressing keys. It should pass both key-down and key-up events to the superview.

Ok, thank you so much now, it works now. But if it is ok, I would just like to check to make sure what I'm doing is the "correct" way so that I don't run into problems further down the line.


I have put the code inside the class above into the class of the first Custom View. The first custom view was would appear at the top if I dragged it onto the top of the other views. Is this what you means by the first responder?


Anyway, I put it into there then the code in the ViewController which was listening for keyDown and keyUp worked. Have I don't this correctly? Thanks again for yout help.

I'm not sure exactly what you're asking. But no, the first responder is not the topmost view—it's whatever's selected and highlighted in the window. Have you seen the blue "focus rings" around text fields, etc. to indicate that they're accepting input? That's the first responder.

Hello,

Try by turning off the Full Keyboard Access in Accessibility system preferences / settings

Why doesn't the keyUp event work in Swift?
 
 
Q