在NSView控制器中获取按键事件

时间:2013-10-17 21:28:34

标签: objective-c cocoa nsviewcontroller nsevent nsresponder

我正在尝试找到一个允许我在视图控制器中获取keydown事件的解决方案。 我不相信默认情况下视图控制器是响应者链的一部分。

我很感激如何解决这个问题。我无法找到关于如何将VC添加到响应者链并获取事件的文档。

感谢。

Miek

3 个答案:

答案 0 :(得分:3)

您可以实现以下内容:

-(void) globalKeyDown: (NSNotification *) notification 
你的控制器类中的

方法,然后只需在控制器的awakeFromNib ...或loadView方法中添加观察者

- (void)awakeFromNib
{
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(globalKeyDown:)
                                                 name:@"my_keyEvent" 
                                               object:nil];
}
在您的视图类中

-(void)keyDown:(NSEvent *)theEvent
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"my_keyEvent"
                                                    object:theEvent
                                                  userInfo:@{@"sender":self}];
}

答案 1 :(得分:2)

NSViewController没有默认方法来执行此操作。但是,您可以通过继承NSView来实现此目的。这是基本的想法:

  • 如果创建视图子类,则可以将视图控制器设置为委托,并创建处理事件的委托方法。
  • 您可以在视图标题的开头声明委托协议。
  • 在视图控制器标头中导入视图标题。将视图控制器声明为实现协议。
  • 在您的视图中keyDown将活动发送给代表。

另一种方法是在NSNotifications中发布keyDown,并在视图控制器中观察和处理通知。其他方式也存在。

解释了代理方法的NSView子类

以下是带有NSView子类的委托示例,该子类在其标头中声明了一个带有一个必需方法的协议,一个符合协议的IBOutlet id属性。 NSView子类在需要时将此方法调用到其委托。如果代表是nil,那在Cocoa中没问题。另请注意,切向上,我已将IB_DesignableIBInspectable添加到视图的颜色属性中。这允许在IB中设置它们并且需要10.10 SDK。

app委托已在NSView实现文件中导入AppDelegate.m子类,并在.m文件顶部的AppDelegate类扩展中采用了该协议。在@implementation部分,它还实现了该方法。

另请注意,在IB中,我向窗口添加了NSView,然后将其类设置为检查器中的自定义NSView子类。最后,我将eventDelegate IBOutlet设置为IB中的AppDelegate代理。

自定义NSView子类接口

#import <Cocoa/Cocoa.h>


@protocol EventDelegatingViewDelegate <NSObject>

- (void)view:(NSView *)aView didHandleEvent:(NSEvent *)anEvent;

@end

IB_DESIGNABLE
@interface EventDelegatingView : NSView

@property IBOutlet id<EventDelegatingViewDelegate> eventDelegate;
@property IBInspectable NSColor *fillColor;
@property IBInspectable NSColor *strokeColor;

@end

自定义NSView子类实现

#import "EventDelegatingView.h"



@implementation EventDelegatingView

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {return YES;}

    // The following two methods allow a view to accept key input events. (literally they say, YES, please send me those events if I'm the center of attention.)
- (BOOL)acceptsFirstResponder {return YES;}
- (BOOL)canBecomeKeyView {return YES;}

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    [self.fillColor set];
    NSRectFill(self.bounds);

    [self.strokeColor set];
    NSFrameRect(self.bounds);
}

    // Notice these don't do anything but call the eventDelegate. I could do whatever here, but I didn't.
    // The NICE thing about delgation is, the originating object stays in control of it sends to its delegate.
    // However, true to the meaning of the word 'delegate', once you pass something to the delegate, you have delegated some decision making power to that delegate object and no longer have any control (if you did, you might have a bad code smell in terms of the delegation design pattern.)
- (void)mouseDown:(NSEvent *)theEvent
{
    [self.eventDelegate view:self didHandleEvent:theEvent];
}

- (void)keyDown:(NSEvent *)theEvent
{
    [self.eventDelegate view:self didHandleEvent:theEvent];
}

@end

App Delegate(和eventDelegate!)实施

#import "AppDelegate.h"
    // Import the view class and if there were other files that implement any protocol
#import "EventDelegatingView.h"

    // Declare protocol conformance (or more accurately, not only import that protocol interface, but say you're going to implement it so the compiler can nag you if you don't)
@interface AppDelegate ()<EventDelegatingViewDelegate>

@property (weak) IBOutlet NSWindow *window;

    // For the simplest demo app we don't even need this property.
@property IBOutlet EventDelegatingView *eventDelegatingView;

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

    // It's all right here. Receive a reference to a view and a reference to an event, then do as you like with them.
#pragma mark - EventDelegatingViewDelegate
- (void)view:(NSView *)aView didHandleEvent:(NSEvent *)anEvent
{
    NSString *interestingEventNote;

    switch (anEvent.type) {
        case NSKeyDown:
        case NSKeyUp:
        {
                // For simplicity we won't try to figure out the modifier keys here.
            interestingEventNote = [NSString stringWithFormat:@"%@ key was pressed.", anEvent.charactersIgnoringModifiers];
        }
            break;

        case NSLeftMouseDown:
        {
            interestingEventNote = [NSString stringWithFormat:@"Left mouse down at point %@ in window", NSStringFromPoint(anEvent.locationInWindow)];
        }
            break;

        default:
            break;
    }

    NSLog(@"%@ %@ aView=%@\n note=%@", self, NSStringFromSelector(_cmd), aView, interestingEventNote?interestingEventNote:@"Nothing worth noting");
}

@end

这就是授权的力量。基本上它是各种各样的回调,并且是一种很好的方法来构建一个类,使它能够按照需要将其他东西推迟。以一种相当懒惰,开放和松散耦合的方式将一些业务逻辑移动到正确的位置。

注意:我的代码示例显示了使用应用委托。但校长是一样的。视图控制器只是一个委托,您可以根据需要添加多少。

答案 2 :(得分:2)

在您的NSWidow(或NSWindowController)类实现中,将您的视图控制器设置为第一响应者:

[self makeFirstResponder:yourViewControllerInstance];

当然,您必须使您的NSViewController类对acceptsFirstResponder消息返回YES。