【问题标题】:NSStatusBarButton keep highlightedNSStatusBarButton 保持突出显示
【发布时间】:2014-09-23 21:03:28
【问题描述】:

从 OS X 10.10 开始,大部分 NSStatusItem 已被弃用,取而代之的是由 NSStatusBarButton 组成的 button 属性。它应该像普通按钮一样工作,但不幸的是,NSStatusButton 中的cellsetCell 方法也已被弃用。因此,我正在努力寻找一种方法来在单击按钮后保持按钮突出显示(通常该按钮在鼠标按下时突出显示,而在鼠标抬起时不突出显示。我想在鼠标抬起后保持突出显示)。

在其操作中调用[NSStatusButton setHighlighted:] 不起作用,因为一旦鼠标抬起,它似乎就不会突出显示自身。另一方面,使用延迟在下一个循环(即[self performSelector: withDelay:])上调用它会导致高光以一种相当难看的方式闪烁。它可以工作,但看起来不太好。

将按钮类型设置为NSToggleButton 会完全删除突出显示,而是突出显示奇怪的模板图像。

这些是我能想到的唯一方法。有没有办法覆盖这个 NSButtonCell mouseUp 行为?

【问题讨论】:

标签: objective-c macos cocoa osx-yosemite nsstatusitem


【解决方案1】:

这里还有一个选项。不要设置NSStatusItemaction 属性。而是添加一个本地事件监视器:

[NSEvent addLocalMonitorForEventsMatchingMask:(NSLeftMouseDown | NSRightMouseDown)
                                      handler:^NSEvent *(NSEvent *event) {
                                          if (event.window == self.statusItem.button.window) {
                                              [self itemClicked];
                                              return nil;
                                          }
                                          return event;
                                      }];

然后在-itemClicked 中使用highlight: 方法突出显示按钮:

- (void)itemClicked {
    [self.statusItem.button highlight:YES];
    // Do other stuff 
}

要取消突出显示,只需在需要的地方调用按钮的highlight:NO

【讨论】:

  • 我搜索了很多,这是唯一的答案。谢谢。
  • 这仍然适用于 Catalina。匹配窗口后不要忘记return nil
【解决方案2】:

Swift 3 版本的Manfred Urban's 答案。 在 El Capitan 上工作。

extension NSStatusBarButton {

   public override func mouseDown(_ event: NSEvent) {

        if (event.modifierFlags.contains(NSControlKeyMask)) {
            self.rightMouseDown(event)
            return
        }

        self.highlight(true)

        (self.target as? TrivialTargetClass)?.togglePopover()
    }
}

如果合适,不要忘记将按钮高亮属性再次设置为 false。

【讨论】:

【解决方案3】:

我为状态项添加了一个子视图,并在该视图中添加了 mouseDown 等事件处理程序,称为 [[statusItem button] highlight:true]。事实证明 setHighlighted: 和 highlight: 不一样。

NSArray *array = [NSArray arrayWithObjects:[statusItem button], [self statusItemView], nil];
[[[statusItem button] superview] setSubviews:array];
//Highlight like so:
[[statusItem button] highlight:true];

编辑:从 El Capitan 开始,此方法不再有效,statusItem.button.highlight = true 也不再有效??

【讨论】:

    【解决方案4】:

    我自己也在努力解决这个问题,我发现在NSStatusBarButton 上的一个类别中覆盖mouseDown: 有效:

    #import "MUTargetClass.h"
    
    @implementation NSStatusBarButton (Additions)
    
    - (void)mouseDown:(NSEvent *)theEvent
    {
        // Relay CTRL+Click to perform a right click action
        if(theEvent.modifierFlags & NSControlKeyMask)
        {
            [self rightMouseDown:theEvent];
            return;
        }
    
        // Handle highlighting
        [self setHighlighted:YES];
    
        // Perform action on target
        [(MUTargetClass *)self.target actionSelector:self];
    }
    
    @end
    

    MUTargetClass 然后可以例如实现:

    #import "NSStatusBarButton+Additions.h"
    
    @implementation MUTargetClass
    
    […]
        self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
        NSStatusBarButton *button = [self.statusItem button];
        [button setTarget:self];
    […]
    
    - (void)actionSelector:(id)sender
    {
        // Whatever behavior a click on the button should invoke
    }
    
    […]
        // Reset button's highlighting status when done
        [[self.statusItem button] setHighlighted:NO];
    […]
    
    @end
    

    请注意,在mouseDown: 覆盖中,CTRL+单击按钮的功能会丢失。如上图,可以通过将事件转发到rightMouseDown:来恢复。

    调用动作的更简单方法是类似于类别中的[self.target performSelector:self.action] 和目标类中的[self.statusItem setAction:@selector(actionSelector:)],但在ARC 项目中是may cause a leak

    编辑:这也适用于 El Capitan。

    【讨论】:

    • @xhacker:在 OS X 10.11 上测试。
    【解决方案5】:

    TL;DR:NSStatusItem 属性中的任何NSButton 实例(带有NSImagetemplate=YES)在视觉上看起来与NSStatusBarButton 完全相同。你可以随意控制他们的highlight属性。

    如果您想手动控制NSStatusItem 的突出显示,同时获得使用NSStatusBarButton 的所有外观优势,您可以使用稍微不同的方法。 您可以创建自己的 NSButton 实例,该实例具有自己的属性 highlight,完全在您的控制之下。然后您必须创建NSImage 实例并将其template 属性设置为YES。然后你必须将此button 添加到[NSStatusItem view](是的,这是softly deprecated),甚至作为系统创建[NSStatusItem button] 的子视图。在此之后,您必须使用[NSStatusItem drawStatusBarBackgroundInRect:withHighlight:] 手动绘制NSStatusItem 的背景(这也已被弃用,哦)。

    通过这种方法,您可以结合对NSStatusItem 的外观和感觉的完全控制,并获得按钮图像的自动样式。

    【讨论】:

    • 我花了一整天的时间试图解决较新的 API,但感谢您的评论,我尝试了已弃用的 drawStatusBarBackgroundInRect:withHighlight: API,在我看来它工作得更好。我很确定 Apple 仍然使用已弃用的 API 作为其菜单栏图标,因为突出显示仍然存在。很奇怪,Apple 只会在新的NSStatusBarButton 中闪烁亮点。
    【解决方案6】:

    Anton 的解决方案是完美的。这是在 Swift 3 中:

    NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { [weak self] event in
        if event.window == self?.statusItem.button?.window {
            // Your action:
            self?.togglePopover(self?.statusItem.button)
            return nil
        }
    
        return event
    }
    

    我为NSApplicationWillResignActive 添加了一个观察者来关闭弹出框并将按钮的isHighlighted 设置为false

    【讨论】:

      【解决方案7】:

      一个想法是混合isHighlightedNSStatusBarButtonCell 并返回一个任意值。 https://gist.github.com/questbeat/3244601c8dc779484076

      但问题是苹果的审查团队是否允许这种方法......

      【讨论】:

        【解决方案8】:

        卢克的回答很棒。我只是在此基础上分享我的实现。

        NSButton *button = [[NSButton alloc] initWithFrame:self.statusItem.button.frame];
        button.alphaValue = 0;
        NSArray *array = @[self.statusItem.button, button];
        self.statusItem.button.superview.subviews = array;
        
        [button sendActionOn:(NSLeftMouseDownMask | NSRightMouseDownMask)];
        button.target = self;
        button.action = @selector(statusItemClicked);
        

        【讨论】:

          【解决方案9】:

          如果您将按钮的突出显示安排在主线程上的后续执行中,那么一切似乎都可以解决。这也适用于 El Capitan。

              if self.appPopover.shown {
                  self.appPopover.performClose(sender)
              } else {
                  if let button = statusItem.button {
                      // set the button's highlighted property to true
                      dispatch_async(dispatch_get_main_queue(), {
                          button.highlighted = true
                      })
          
                      appPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: NSRectEdge.MinY)
                  }
              }
          

          这确实有效,但您可能会注意到由于按钮状态从 ON 更改为 OFF 然后再次 ON 导致的轻微闪烁。关闭是由于弹出窗口显示而发生的。因此,要解决这个问题,只需在将按钮设置为突出显示后立即将 appPopover.showRelativeToRect() 调用移到 dispatch_async() 块内。

          【讨论】:

          • 闪烁仍然出现。
          【解决方案10】:

          这是@erikvdplas 答案的 Swift 5 版本。我在 macOS 11.4 Big Sur 上对此进行了测试。

          extension NSStatusBarButton {
              public override func mouseDown(with event: NSEvent) {
                  super.mouseDown(with: event)
                  self.highlight((NSApp.delegate as! YourAppDelegateClass).popover.isShown)
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-02-19
            • 1970-01-01
            • 2014-11-16
            • 2013-12-09
            • 1970-01-01
            • 1970-01-01
            • 2014-06-04
            • 2013-11-18
            相关资源
            最近更新 更多