【问题标题】:Getting the initiator for an NSMenuItem获取 NSMenuItem 的启动器
【发布时间】:2014-11-27 19:59:09
【问题描述】:

我有 3 张图片的视图,它们的班级中有自己的 @property。 当我右键单击其中一张图片时,我会显示一个带有NSMenuItems 子菜单的NSMenu。 所有项目都发送到名为- (IBAction)selectImage:(id)senderIBAction,每个项目都有不同的tagsender 当然是NSMenuItem。如何找出用户右键单击的图像?所以我基本上是在寻找sender的父级的调用者。

或者,我在构建菜单时全错了?

【问题讨论】:

    标签: objective-c macos cocoa desktop-application nsmenu


    【解决方案1】:

    您可以在视图类中覆盖-menuForEvent: 并存储对事件的引用、记住位置,或者确定击中了哪个图像并记住它。然后,在 action 方法中,您将使用记住的事件/位置/图像索引来确定如何响应。

    当然,要在视图坐标中获取事件位置,您可以这样做:

    NSPoint point = [self convertPoint:event.locationInWindow fromView:nil];
    

    当测试该点是否在视图的特定部分时,您应该使用:

    if ([self mouse:point inRect:rectOfInterest])
        // ...
    

    【讨论】:

      【解决方案2】:

      您以错误的方式解决问题。不要在菜单项上设置明确的目标,而是使用First Responder(或nil,如果你在代码中这样做)。

      默认情况下,第一响应者将是拥有上下文菜单的视图(“对于上下文菜单,搜索仅限于显示上下文菜单的窗口的响应者链,从关联的视图开始”根据documentation)。

      因此,如果您将NSImageView 子类化并在那里实现您需要的操作,默认情况下它将接收消息。它们可以是一个简单的重定向到你的控制器对象,但你会知道以这种方式点击了哪个视图(因为在这种情况下它是self!)


      这个答案是对这个先前答案的编辑,明显不那么优雅:

      首先创建一个 NSMenu 的子类(我们称之为PPMenu),它有一个附加属性来存储对图像视图的弱引用(我们称之为imageView)。将该类设置为 XIB 中的菜单类,并将菜单也设置为图像视图的上下文菜单(或者,如果 XIB 中不存在它们,只需添加一个出口)。

      现在,如果您覆盖 -menuForEvent: 是您的 NSImageView 子类,您将能够自定义上下文菜单...首先在此处调用 super 以检索 PPMenu 实例(如果已设置在 IB 中,否则使用插座),并设置您已添加到 selfimageView 属性(因为您在图像视图代码中)。

      现在,在您的操作代码中,您可以使用((PPMenu*)(sender.menu)).imageView 来检索与菜单关联的图像视图。

      【讨论】:

        【解决方案3】:

        只需记住您在右键单击事件处理程序中单击的图像即可。

        #pragma mark - mouse events
        
        - (void)rightMouseDown:(NSEvent *)event
        {
          [super rightMouseDown:event]; // if you don't want to push events to parent view - remove this line
          NSPoint eventLocation = [event locationInWindow]; // get mouse location in window coordinates
          NSPoint localLocation = [self convertPoint:eventLocation fromView:nil]; // get mouse location in view coordinates
          selectedImageView = nil; // set it nil each time right mouse down
          NSArray *images = @[imageView0, imageView1, imageView2]; // all your image view outlets, check if they aren't nil
          for (NSImageView *imageView in images)
            if (NSPointInRect(localLocation, imageView.frame))
            {
              selectedImageView = imageView; // selectedImageView should be a class member
              break;
            }
        }
        
        #pragma mark - menu actions
        
        - (IBAction)selectImage:(id)sender
        {
          if (selectedImageView)
          {
            // your code here
          }
        }
        

        这是您需要做的简单版本。但是让我们假设用户在 image0 附近按下鼠标右键,然后将光标拖离图像,然后释放鼠标。为此,您应该覆盖另一个鼠标事件处理程序 rightMouseUp 只是为了检查光标仍然在 selectedImageView 上方

        #pragma mark - mouse events
        
        //...
        
        - (void)rightMouseUp:(NSEvent *)event
        {
          [super rightMouseUp:event];
          if (!selectedImageView)
            return; // nothing to check here
          NSPoint eventLocation = [event locationInWindow]; 
          NSPoint localLocation = [self convertPoint:eventLocation fromView:nil]; 
          if (!NSPointInRect(localLocation, selectedImageView.frame)) // the mouse not above the selectedImageView
            selectedImageView = nil;
          else
            {
              // there may be NSMenu instance initializing
            }
        }
        

        【讨论】:

        • 使用-rightMouseDown: 是不够的。上下文菜单也可以通过 Control-click 来启动,但不通过该方法。可能还有其他方法,例如可访问性。此外,上下文菜单出现在鼠标按下时。在鼠标上移之前将光标移开并不重要,实际上是正常的。不过,我认为视图不会收到它,因为我认为鼠标是由菜单跟踪捕获的。不过,我还没有检查最后一点。
        • @KenThomases 好吧,你说得对,我只是指出要记住 imageView,根本没有触及菜单实例化过程。
        • 嗯?我并不是说你需要实例化一个菜单。我的观点是,当用户想要上下文菜单时,可能根本不会调用 -rightMouseDown:。因为他们不必右键单击。此外,OP 从未提及图像 views。事实上,建议是单个视图中只有图像。
        • @KenThomases 嗯,我没想到。非常合适的假设。
        猜你喜欢
        • 1970-01-01
        • 2011-07-04
        • 1970-01-01
        • 2012-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-31
        相关资源
        最近更新 更多