Take the 2-minute tour ×

I need to assign a view to an NSMenuItem and do some custom drawing. Basically, I'm adding a little delete button next to the currently selected menu item, among other things. But I want my custom menu item to look and behave like a regular menu item in all other ways. According to the doc:

A menu item with a view does not draw its title, state, font, or other standard drawing attributes, and assigns drawing responsibility entirely to the view.

Ok, so I had to duplicate the look of the state column and the selection gradient, which wasn't that hard. The part I'm having trouble with is the way the menu item "flashes" or "blinks" after it is selected. I'm using an NSTimer to try to mimic this little animation, but it just feels off. How many times does it blink? What time interval should I use? I've experimented a lot and it just feels out of whack.

Has anyone done this before or have other suggestions on how to add a button to a menu item? Maybe there should be a stack exchange site just for custom cocoa drawing...

share|improve this question
You should also flash the menuitem's parent in the menubar when the shortcut is pressed, if it has one. –  user142019 Feb 1 '11 at 16:39
I'm looking for selected item gradient code (or just starting and ending colors), can you share it? Thanks in advance. –  Vladimir Prudnikov Mar 5 '11 at 14:24
add comment

1 Answer

up vote 1 down vote accepted

I know this is over a year old, but this was the first hit on my Google search and was unanswered, so I'm posting my answer for sake of those still looking for a solution.

For my app, I used Core Animation with a custom NSView for the NSMenuItem view. I created a new layer-backed view, set the background color, and added it to my custom view. I then animated the layer (the flashing part). Then in the -(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag callback, I removed the overlay and closed the menu. This doesn't perfectly match the default NSMenu's flash, but I wanted a 37Signals/Stack Overflow Yellow Fade Technique, so it works for me. Here it is in code:

-(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]];

    [selectionOverlayView removeFromSuperview];

    NSMenuItem *enclosingMenuItem = [self enclosingMenuItem];
    NSMenu *enclosingMenu = [enclosingMenuItem menu];
    [enclosingMenu cancelTracking];
    [enclosingMenu performActionForItemAtIndex:[enclosingMenu indexOfItem:enclosingMenuItem]];
share|improve this answer
Great code to start with - there are a few issues here, though. For example, selectionOverlayView is never released after it is allocated, so that memory will leak. Also, instead of doing the group of animations, you could just do a single animation with the "autoreverses" property set to true. But anyway, thanks for posting, it was very helpful. –  AriX Jan 7 '13 at 5:45
Its another year later (almost) and I am looking to do something similar. I am curious if you have had any enhancements to your code, and if you would be willing to share a sample class? Can email me at my username at gmail. Thanks in advance –  kdbdallas Jun 27 '13 at 20:49
add comment

Your Answer


