选择后如何刷新自定义NSMenuItem视图?

时间:2011-02-01 16:34:28

标签: cocoa drawing nsview nsmenuitem

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

  

带有视图的菜单项不会绘制   它的标题,状态,字体或其他   标准图纸属性,和   分配绘图责任   完全是为了观点。

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

之前有没有人这样做过,或者对如何在菜单项中添加按钮有其他建议?也许应该有一个堆栈交换网站只是为了定制可可绘图......

3 个答案:

答案 0 :(得分:3)

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

对于我的应用程序,我使用Core Animation和NSMenuItem视图的自定义NSView。我创建了一个新的图层支持视图,设置背景颜色,并将其添加到我的自定义视图中。然后我为图层(闪烁部分)设置了动画。然后在-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag回调中,我删除了叠加层并关闭了菜单。这与默认的NSMenu闪存不完全匹配,但我想要一个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]];
}

答案 1 :(得分:0)

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

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
}

答案 2 :(得分:0)

实际上,您可以将自定义视图设置为常规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开发的话,可能值得一看。