【问题标题】:Popup menu implementation from NSButtonNSButton 的弹出菜单实现
【发布时间】:2012-03-15 08:46:02
【问题描述】:

我应该怎么做?

我在想……

[NSMenu popUpContextMenu:menu withEvent:event forView:(NSButton *)sender];

【问题讨论】:

    标签: objective-c macos cocoa


    【解决方案1】:

    是的。

    按钮动作调用

    [NSMenu popUpContextMenu:menu withEvent:event forView:(NSButton *)sender];
    

    在哪里

    • menu : 你要显示的菜单
    • sender : 你点击的按钮
    • event :您创建的新 NSEvent

    当您创建新的NSEvent 时,请指定您希望显示弹出菜单的位置。

    【讨论】:

    • 试过了,但是“弹出”菜单到处都是。我希望它恰好出现在按钮下方(例如,像 Google Chrome 中的“新标签”、“新窗口”等菜单),但这个看起来太……高……
    • 是的。正如我上面所说,您可以在创建事件时指定弹出菜单的位置(需要显示的位置)。
    • 您可以使用 [[NSApplication sharedApplication] currentEvent] 进行事件。
    • [self.myButton sendActionOn:NSLeftMouseDownMask] 将在鼠标按下时触发。
    • 我推荐-[NSMenu popUp:...] 而不是类方法。这样,即使您使用键盘快捷键,它也会相对于您的按钮定位。
    【解决方案2】:

    已接受答案的 Swift 版本

    @IBAction func actionOccurred(sender: NSButton) {
        if let event = NSApplication.sharedApplication().currentEvent {
            NSMenu.popUpContextMenu(sender.menu!, withEvent: event, forView: sender)
        }
    }
    

    更新答案

    将 NSMenu 添加到情节提要中的 NSViewController,如图所示

    连接插座

    右键单击故事板中的按钮并将菜单出口连接到我们刚刚添加到故事板中的菜单(图中箭头指向的位置)

    添加 IBAction

    @IBAction func menuClicked(_ sender: NSButton) {
        var location = NSEvent.mouseLocation
        location.x -= 10; location.y -= 10 // Menu appears below the button
        let menu = sender.menu!
        menu.popUp(positioning: menu.item(at: 0), at: location, in: nil)
    }
    

    【讨论】:

      【解决方案3】:

      正如我所评论的,我发现 ButtonMadness 示例并不完美。 我的实现似乎效果更好。菜单在鼠标按下时显示,按钮始终保持按下状态,可以指定菜单位置,菜单在没有后续虚假显示的情况下被关闭。

      说实话,NSPopupButton 在大多数情况下都是更好的选择。我使用这个代码主要是因为有一个类用于按钮和弹出窗口很方便,并且菜单不包含弹出控件图像和标题。我从一个单独的 nib 加载菜单并根据需要在应用程序的其他地方重复使用它。

      请注意,添加对弹出框和菜单的额外支持是微不足道的。

      NSButton subclass:
      
      - (void)mouseDown:(NSEvent *)theEvent {
      
          // if a menu is defined let the cell handle its display
          if (self.menu) {
              if ([theEvent type] == NSLeftMouseDown) {
                  [[self cell] setMenu:[self menu]];
              } else {
                  [[self cell] setMenu:nil];
              }
          }
      
          [super mouseDown:theEvent];
      }
      
      NSButtonCell subclass:
      
      - (BOOL)trackMouse:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp
      {
          // if menu defined show on left mouse
          if ([event type] == NSLeftMouseDown && [self menu]) {
      
              NSPoint result = [controlView convertPoint:NSMakePoint(NSMidX(cellFrame), NSMidY(cellFrame)) toView:nil];
      
              NSEvent *newEvent = [NSEvent mouseEventWithType: [event type]
                                                     location: result
                                                modifierFlags: [event modifierFlags]
                                                    timestamp: [event timestamp]
                                                 windowNumber: [event windowNumber]
                                                      context: [event context]
                                                  eventNumber: [event eventNumber]
                                                   clickCount: [event clickCount]
                                                     pressure: [event pressure]];
      
              // need to generate a new event otherwise selection of button
              // after menu display fails
              [NSMenu popUpContextMenu:[self menu] withEvent:newEvent forView:controlView];
      
              return YES;
          }
      
          return [super trackMouse:event inRect:cellFrame ofView:controlView untilMouseUp:untilMouseUp];
      }
      

      【讨论】:

      • 非常适合我。我发现我可以直接在单元格上设置菜单,根本不需要子类化按钮。我是否缺少某些原因,这将是一个问题?
      【解决方案4】:

      最近,我试图实现它,但我想,我带来了一个更简单的解决方案

      -(IBAction)buttonClick:(id)sender {
          NSButton * b = (NSButton*)sender;
          NSPoint l = [ self.window convertBaseToScreen:b.frame.origin ];
      
          [ self.menu popUpMenuPositioningItem:nil atLocation:l inView:nil ];
      }
      

      更新

      convertBaseToScreen 从 10.7 开始被弃用,而不是使用 convertRectToScreen,如下所示:

      NSPoint l = [self.window convertRectToScreen:b.frame].origin; 
      

      【讨论】:

      • convertBaseToScreen 自 10_7 起已弃用
      • 使用故事板(在 ViewController 中)它似乎不起作用。我试过没有成功:NSPoint point = [[self.view window] convertRectToScreen:((NSButton*)sender).frame].origin; 有什么想法?
      • @Joannes 检查您是否已正确设置所有内容,即您的操作已连接并正在被调用,self.menu 不为零,self.view.window 不为零。你得到什么价值(点)?
      【解决方案5】:

      在操作调用上使用上下文菜单并不是一个好方法,因为菜单直到 mouseUp 才会显示 - 您不会获得按住和拖动菜单的行为。 Apple 的 ButtonMadness 示例展示了如何在 NSButton 的子类中真正做到这一点,请参阅 DropDownButton。 https://developer.apple.com/library/mac/samplecode/ButtonMadness/Introduction/Intro.html

      总结该子类:创建一个 NSPopUpButtonCell 并将 pullsDown 设置为 YES 并将preferredEdge 设置为 NSMaxYEdge,复制您的菜单以添加一个空白顶部项目并将其设置为该单元格的菜单,在 mouseDown 调用 [thePopUpCell performClickWithFrame:self.bounds inView:self ] 并设置 self.needsDisplay

      【讨论】:

      • 至少在 10.8 上,DropDownButton 代码有缺陷。要突出显示该问题,请构建 ButtonMadness 示例并单击 DropDownButton。菜单将显示 - 不要选择项目。现在单击单选按钮 2 - 隐藏菜单但未选择单选项目。所以再次单击,菜单再次显示。再次单击它会隐藏等。
      • 为什么要添加空白项?我很感激你这样做(未能添加它会截断菜单),但我不知道为什么
      【解决方案6】:

      这样做。

          -(IBAction)onClickSourceAdd:(id)sender {
             NSMenu *mainMenu = [NSApp mainMenu];
             NSMenu *sourceMenu = [[mainMenu itemAtIndex:2] submenu];
             NSMenu *addMenu = [[sourceMenu itemAtIndex:0] submenu];
             [NSMenu popUpContextMenu:addMenu 
                     withEvent:[NSApp currentEvent] 
                     forView:(NSButton *)sender]; 
           }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-13
        • 1970-01-01
        • 1970-01-01
        • 2015-04-12
        • 1970-01-01
        相关资源
        最近更新 更多