【问题标题】:How to flash a custom NSMenuItem view after selection?选择后如何闪烁自定义 NSMenuItem 视图?
【发布时间】:2016-11-30 01:00:13
【问题描述】:

我需要为 NSMenuItem 分配一个视图并进行一些自定义绘图。基本上,我在当前选定的菜单项旁边添加了一个小删除按钮,等等。但我希望我的自定义菜单项在所有其他方面看起来和行为都像常规菜单项。根据文档:

带有视图的菜单项不绘制 它的标题、状态、字体或其他 标准绘图属性,以及 分配绘图责任 完全看得出。

好的,所以我不得不复制状态列的外观和选择渐变,这并不难。我遇到问题的部分是菜单项在选择后“闪烁”或“闪烁”的方式。我正在使用 NSTimer 来尝试模仿这个小动画,但它只是感觉不对劲。它闪烁多少次?我应该使用什么时间间隔?我已经尝试了很多,但感觉不正常。

之前有没有人这样做过,或者对如何向菜单项添加按钮有其他建议?也许应该有一个专门用于自定义可可绘图的堆栈交换站点...

【问题讨论】:

  • 当快捷方式被按下时,您还应该在菜单栏中闪烁菜单项的父项(如果有的话)。
  • 我正在寻找所选项目的渐变代码(或者只是开始和结束颜色),你能分享一下吗?提前致谢。

标签: cocoa drawing nsview nsmenuitem


【解决方案1】:

我知道这已经超过一年了,但这是我在 Google 搜索中的第一次点击并且没有得到答复,所以我发布我的答案是为了那些仍在寻找解决方案的人。

对于我的应用程序,我将 Core Animation 与自定义 NSView 一起用于 NSMenuItem 视图。我创建了一个新的图层支持视图,设置了背景颜色,并将其添加到我的自定义视图中。然后我为图层设置动画(闪烁的部分)。然后在-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 回调中,我删除了覆盖并关闭了菜单。这与默认 NSMenu 的 flash 不完全匹配,但我想要一个 37Signals/Stack Overflow Yellow Fade Technique,所以它对我有用。代码如下:

-(void) mouseUp:(NSEvent *)theEvent {
    CALayer *layer = [CALayer layer];
    [layer setDelegate:self];
    [layer setBackgroundColor:CGColorCreateGenericRGB(0.0, 0.0, 1.0, 1.0)];

    selectionOverlayView = [[NSView alloc] init];
    [selectionOverlayView setWantsLayer:YES];
    [selectionOverlayView setFrame:self.frame];
    [selectionOverlayView setLayer:layer];
    [[selectionOverlayView layer] setNeedsDisplay];
    [selectionOverlayView setAlphaValue:0.0];
    [self addSubview:selectionOverlayView];

    CABasicAnimation *alphaAnimation1 = [CABasicAnimation animationWithKeyPath: @"alphaValue"];
    alphaAnimation1.beginTime = 0.0;
    alphaAnimation1.fromValue = [NSNumber numberWithFloat: 0.0];
    alphaAnimation1.toValue = [NSNumber numberWithFloat: 1.0];
    alphaAnimation1.duration = 0.07;

    CABasicAnimation *alphaAnimation2 = [CABasicAnimation animationWithKeyPath: @"alphaValue"];
    alphaAnimation2.beginTime = 0.07;
    alphaAnimation2.fromValue = [NSNumber numberWithFloat: 1.0];
    alphaAnimation2.toValue = [NSNumber numberWithFloat: 0.0];
    alphaAnimation2.duration = 0.07;

    CAAnimationGroup *selectionAnimation = [CAAnimationGroup animation];
    selectionAnimation.delegate = self;
    selectionAnimation.animations = [NSArray arrayWithObjects:alphaAnimation1, alphaAnimation2, nil];
    selectionAnimation.duration = 0.14;
    [selectionOverlayView setAnimations:[NSDictionary dictionaryWithObject:selectionAnimation forKey:@"frameOrigin"]];

    [[selectionOverlayView animator] setFrame:[selectionOverlayView frame]];
}

-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    [selectionOverlayView removeFromSuperview];

    NSMenuItem *enclosingMenuItem = [self enclosingMenuItem];
    NSMenu *enclosingMenu = [enclosingMenuItem menu];
    [enclosingMenu cancelTracking];
    [enclosingMenu performActionForItemAtIndex:[enclosingMenu indexOfItem:enclosingMenuItem]];
}

【讨论】:

  • 伟大的代码开始 - 但这里有一些问题。比如 selectionOverlayView 分配后就永远不会释放,这样就会导致内存泄漏。此外,您可以只做一个动画,并将“autoreverses”属性设置为 true,而不是做一组动画。但无论如何,感谢您的发布,它非常有帮助。
  • 又过了一年(几乎),我想做类似的事情。我很好奇您是否对您的代码进行了任何改进,并且您是否愿意分享一个示例类?可以通过我在 gmail 的用户名给我发电子邮件。提前致谢
【解决方案2】:

实际上可以让您的自定义视图像普通的NSMenuItem 一样闪烁,而无需手动实现动画。

注意:这使用了一个私有 API,并且还修复了一些与自定义视图相关的其他奇怪的 NSMenuItem 怪癖。

NSMenuItem.h

#import <AppKit/AppKit.h>

@interface NSMenuItem ()
    - (BOOL)_viewHandlesEvents;
@end

桥接头

#import "NSMenuItem.h"

MenuItem.swift

class MenuItem: NSMenuItem {
    override func _viewHandlesEvents() -> Bool {
        return false
    }
}

这个 API 确实应该是公开的,如果您不是为 App Store 开发,它可能值得一看。

【讨论】:

    【解决方案3】:

    这是我闪烁自定义菜单项的代码。

    int16_t   fireTimes;
    BOOL      isSelected;
    
    - (void)mouseEntered:(NSEvent*)event
    {
        isSelected = YES;
    }
    
    - (void)mouseUp:(NSEvent*)event {
    
        fireTimes = 0;
    
        isSelected = !isSelected;
        [self setNeedsDisplay:YES];
    
        NSTimer *timer = [NSTimer timerWithTimeInterval:0.05 target:self selector:@selector(animateDismiss:) userInfo:nil repeats:YES];
    
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
    }
    
    -(void)animateDismiss:(NSTimer *)aTimer
    {
        if (fireTimes <= 2) {
            isSelected = !isSelected;
            [self setNeedsDisplay:YES];
        } else {
            [aTimer invalidate];
            [self sendAction];
        }
    
        fireTimes++;
    }
    
    - (void)drawRect:(NSRect)dirtyRect {
    
        if (isSelected) {
            NSRect frame = NSInsetRect([self frame], -4.0f, -4.0f);
            [[NSColor selectedMenuItemColor] set];
            NSRectFill(frame);
            [itemNameFld setTextColor:[NSColor whiteColor]];
        } else {
            [itemNameFld setTextColor:[NSColor blackColor]];
        }
    
    }
    
    - (void)sendAction
    {
        NSMenuItem *actualMenuItem = [self enclosingMenuItem];
    
        [NSApp sendAction:[actualMenuItem action] to:[actualMenuItem target] from:actualMenuItem];
    
        NSMenu *menu = [actualMenuItem menu];
        [menu cancelTracking];
    
        //  [self setNeedsDisplay:YES]; // I'm not sure of this
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-27
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 2020-03-04
      • 2011-08-10
      相关资源
      最近更新 更多