【问题标题】:Mouse Enter/Exit events on partially hidden NSViews部分隐藏的 NSView 上的鼠标进入/退出事件
【发布时间】:2012-04-03 02:16:08
【问题描述】:

我有一个问题,我认为可以通过一些黑客来解决,但我很好奇是否有更简单的方法来完成工作而不必做所有这些。

我有一堆 NSViews(层支持,如果这有助于提供更好的解决方案),如下所示:

这里的问题是,这本质上是一个菜单,但对悬停敏感。如果用户将鼠标悬停在较低级别视图的暴露部分之一上,我需要根据该视图的内容执行操作。它是一个动态系统,因此像这样堆叠的菜单项的数量可能会发生变化,从而使静态计算更加困难。如您所见,它们基本上都是第一个项目的副本(形状方面),但随后通过简单的变换旋转在堆栈中向下旋转一点。

我向 SO 社区提出的问题是,你们都认为获得 mouseEntered: 和 mouseExited: 事件的最佳方法是这些视图的字面可见部分?

我试图做的是在这些视图的 visibleRect 部分上使用 NSTrackingArea,这听起来比在这种情况下实际要方便得多。实际上,visibleRect 似乎一直对它们所有人“可见”。除了部分重叠的 NSView 之外,没有任何东西被明确阻止或隐藏。所发生的一切是我从所有视图中得到一个垃圾邮件控制台,立即尖叫鼠标进入他们的矩形。

我正在考虑的事情是制作每个菜单项的子 NSView,并让每个菜单项负责跟踪区域......每个菜单项的右侧和底部都有一个可以报告的“条形”视图,但是这仍然是一个 hack 并且是 icky。

有人有更好的主意吗?也许是经验之谈?

谢谢!

【问题讨论】:

    标签: objective-c macos cocoa mouse nsview


    【解决方案1】:

    我知道您已经有了解决方案,但我想我会尝试一种不同的方法,它不需要获取大量 mouseMoved 事件。我在代码中创建了 3 个自定义视图,为它们添加了跟踪矩形,并将所有 mouseEntered 和 mouseExited 消息发送到执行 hitTest 以确定哪个视图最高的同一方法。这是窗口内容视图的代码。

    @implementation MainView
    @synthesize oldView;
    
    -(void)awakeFromNib {
        oldView = nil;
        Card *card1 = [[Card alloc]initWithFrame:NSMakeRect(150, 150, 200, 150) color:[NSColor redColor] name:@"Red Box"];
        NSTrackingArea *area1 = [[NSTrackingArea alloc]initWithRect:card1.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
        [self addTrackingArea:area1];
        [self addSubview:card1];
    
        Card *card2 = [[Card alloc]initWithFrame:NSMakeRect(180, 120, 200, 150) color:[NSColor yellowColor] name:@"Yellow Box"];
        NSTrackingArea *area2 = [[NSTrackingArea alloc]initWithRect:card2.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
        [self addTrackingArea:area2];
        [self addSubview:card2];
    
        Card *card3 = [[Card alloc]initWithFrame:NSMakeRect(210, 90, 200, 150) color:[NSColor greenColor] name:@"Green Box"];
        NSTrackingArea *area3 = [[NSTrackingArea alloc]initWithRect:card3.frame options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp owner:self userInfo:nil];
        [self addTrackingArea:area3];
        [self addSubview:card3];
    }
    
    -(void)mouseEntered:(NSEvent *)theEvent {
        [self reportTopView:theEvent];
    }
    
    -(void)mouseExited:(NSEvent *)theEvent {
        [self reportTopView:theEvent];
    }
    
    -(void)reportTopView:(NSEvent *)theEvent {
        id topView = [self hitTest:[theEvent locationInWindow]];
        if (![topView isEqual:oldView]) {
            oldView = topView;
            ([topView isKindOfClass:[Card class]])? NSLog(@"%@",[(Card *)topView name]):NULL;
        }
    }
    

    这是我称之为卡片(彩色矩形)的代码:

    @implementation Card
    @synthesize name,fillColor;
    
    - (id)initWithFrame:(NSRect)frame color:(NSColor *)color name:(NSString *)aName{
        self = [super initWithFrame:frame];
        if (self) {
            self.fillColor = color;
            self.name = aName;
        }
        return self;
    }
    
    - (void)drawRect:(NSRect)rect {
        [self.fillColor drawSwatchInRect:rect];
    
    }
    

    【讨论】:

    • 仅供参考,您永远不会释放分配的对象。
    • @jp.rider63,是的,那是因为我正在使用 ARC。
    【解决方案2】:

    我终于通过 Steven Troughton-Smith 在 Twitter 上找到了一个解决方案。以下是它的工作原理:

    在每个菜单项中,我忽略了与 NSTrackingArea 或直接鼠标位置解释相关的任何内容。相反,父控制器视图正在处理所有的跟踪和接收鼠标移动事件。

    每个菜单项都有一个被覆盖的 hitTest: 方法,该方法进行点转换并返回被测试的点是否在背景图像内(那里有阴影和东西,比普通实现更难)。

    然后我在控制器中设置了一种“悬停菜单项已更改”回调,以便我可以处理悬停菜单更改。

    这是一个非常简单的解决方案。很高兴我决定停下来问问,而不是用我之前的想法来破解一些东西。

    谢谢史蒂文!

    【讨论】:

      【解决方案3】:

      重叠跟踪区域:

      您所要做的就是从您所在的视图中点击测试。如果这是真的:

       window.view.hitTest(window.mousePos) === self/*sudo code*/
      

      这段代码的作用是返回鼠标位置下的视图。现在您所要做的就是设置一些“if”和“else”子句来验证您的鼠标是关闭还是在视图上。

      完整代码示例:
      https://gist.github.com/eonist/537ae53b86d5fc332fd3

      这里对概念的完整描述:(永久链接)
      http://stylekit.org/blog/2015/12/20/Overlapping-tracking-areas/

      VS 默认进入和退出行为:

      【讨论】:

        【解决方案4】:

        我不得不为这个问题添加另一个答案,因为这是解决问题的另一种方法。这种方法现在还包括路径断言(考虑带有圆边或其他自定义路径的矩形)

        答案是冗长的,但它有效:

        http://stylekit.org/blog/2016/01/28/Hit-testing-sub-views/

        它涉及使用苹果提供的方法:CGPathContainsPoint(path,transform,point)

        如果您点击该博客文章的链接,然后从那里检查 github 上的 styleKit 存储库。您会发现实现上面给出的 gif 动画示例所需的代码。我将其作为指向答案的指针,因为与尝试自己研究此问题相比,它可能花费的时间要少得多。我在我的所有 UI 元素中都使用了这种技术,并且它完美无缺。

        【讨论】:

          猜你喜欢
          • 2011-06-09
          • 1970-01-01
          • 2015-01-01
          • 2017-04-28
          • 2011-01-27
          • 2011-05-06
          • 1970-01-01
          • 2017-07-28
          • 2011-09-29
          相关资源
          最近更新 更多