2

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

具有视图的菜单项不绘制其标题、状态、字体或其他标准绘图属性,而是将绘图职责完全分配给视图。

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

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

4

3 回答 3

4

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

对于我的应用程序,我将 Core Animation 与自定义 NSView 一起用于 NSMenuItem 视图。我创建了一个新的图层支持视图,设置了背景颜色,并将其添加到我的自定义视图中。然后我为图层设置动画(闪烁的部分)。然后在-(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]];
}
于 2012-08-22T19:43:20.407 回答
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 开发,它可能值得一看。

于 2016-11-29T17:00:13.213 回答
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
}
于 2015-03-10T16:03:01.513 回答