4

我想设计一个有点像弹出框的面板:当鼠标在它外面时,它可能会关闭或隐藏自己。

但我不知道如何实现这一目标。我所知道的是视图可以处理-mouseDown,-mouseUp等。但是当鼠标放在其他地方时?我不知道如何捕捉这个事件。


进一步讨论Bavarious

我实际上正在研究状态栏。我关注了一个问题。还有我研究的示例代码。

正如示例代码所做的那样,我重写了前面描述的视图并使用状态栏项的-setView:方法对其进行了设置。我工作中的大部分代码与示例代码完全相同。以下是我认为与我的困惑有关的一些代码部分(顺便说一句,使用了 ARC):

...
@property (nonatomic) SEL disclickAction;     // Called when "dismissed"
@property (nonatomic) SEL action;             // Called when selected
@property (nonatomic, assign) id target;  
...


- (void)dealloc
{
    NSLog(@"%@ dealloc", self);
    [self invalidate];
    //[super dealloc];
}

- (void)mouseDown:(NSEvent *)theEvent
{
    [self setHighlighted:![self isHighlighted]];
    if (_target && _action &&
        [_target respondsToSelector:_action])
    {
        [NSApp sendAction:_action to:_target from:self];
    }
}

// Here is the code that Bavarious taught me:
- (void)setDisclickAction:(SEL)disclickAction
{
    _disclickAction = disclickAction;

    if (!_mouseEventMonitor)
    {
        if (_disclickAction)
        {
            self.mouseEventMonitor = [NSEvent
                           addLocalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask) 
                           handler:^NSEvent *(NSEvent *event) {
                if (event.window != self.window)
                {
                    [self actionDisclick:nil];
                }
                return event;
            }];
        
            [[NSNotificationCenter defaultCenter]
             addObserver:self
             selector:@selector(actionDisclick:)
             name:NSApplicationDidResignActiveNotification
             object:nil];
        }
    }
    else if (!_disclickAction)  // cancel operation
    {
        [NSEvent removeMonitor:_mouseEventMonitor];
        _mouseEventMonitor = nil;
    }
}

这是我的屏幕,例如(黄色卡通脸是我的状态栏项):

当鼠标在上面的位置按下时:
A:视图mouseDown被调用,然后鼠标按下事件的本地观察者被通知。
B:通知鼠标按下事件的本地观察者。
C:通知辞职申请事件。
D:没有事件。鼠标按下事件的本地观察者都不是。这就是问题所在。

4

2 回答 2

11

AppKit 负责将鼠标事件转发到发生这些事件的窗口/视图。如果您希望在其他位置捕获鼠标事件,您可以使用本地事件监视器。

在应该实现此行为的类(可能是拥有面板的类)中,定义一个实例变量或声明的属性来保存一个事件监视器实例:

@property (nonatomic, strong) id mouseEventMonitor;

显示面板时,添加鼠标事件监视器:

self.mouseEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask) handler:^NSEvent *(NSEvent *event) {
    if (event.window != self.panelWindow)
        [self dismissPanel];

    return event;
}];

当您关闭面板时,移除鼠标事件监视器:

- (void)dismissPanel {
    if (self.mouseEventMonitor != nil) {
        [NSEvent removeMonitor:self.mouseEventMonitor];
        self.mouseEventMonitor = nil;
    }

    // …
}

如果您需要测试特定视图而不是包含面板的整个窗口,请使用-[NSView hitTest:]检查鼠标位置 ( event.locationInWindow) 是否属于该视图或其子视图之一。


编辑:为了在用户在应用程序窗口之外单击时关闭面板,观察NSApplicationDidResignActiveNotification,每当应用程序退出其活动状态时发布,这意味着其他一些应用程序已变为活动状态。当您显示面板时:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissPanel) name:NSApplicationDidResignActiveNotification object:nil];

当您关闭面板时:

[[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidResignActiveNotification object:nil];

编辑:要处理用户是否单击了菜单栏(由于应用程序仍处于活动状态而不会发布NSApplicationDidResignActiveNotification)的情况,您还必须观察NSMenuDidBeginTrackingNotification主菜单发布的[NSApp mainMenu]. 当您显示面板时:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissPanel) name:NSMenuDidBeginTrackingNotification object:[NSApp mainMenu]];

当您关闭面板时:

[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMenuDidBeginTrackingNotification object:[NSApp mainMenu]];
于 2013-06-05T02:22:21.230 回答
-3

您可以创建作为 UIButton 的透明或半透明背景视图。您可以在其上放置任何视图元素。向按钮添加处理程序以处理关闭或隐藏视图。像这样的东西:

- (id)init
{
self = [super init];
    if(self)
    {
        [self createBackground];
        [self createUIElements]; 
    }
    return self;
}

- (void)createBackground
{
    UIButton *backgroundButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    [backgroundButton addTarget:self forSelector:@selector(closeOrHide) forControlEvent:UIControlEventTouchUpInside];
    [self addSubview:backgroundButton];

    //If no-arc    [backgroundButton release]; backgroundButton = nil
}

- (void)closeOrHide
{
    //Do stuff
}
于 2013-06-05T02:28:04.873 回答