【问题标题】:Gap above NSMenuItem custom viewNSMenuItem 自定义视图上方的间隙
【发布时间】:2011-05-30 20:21:09
【问题描述】:

我在NSMenuItem 上使用setView: 方法来设置自定义视图。在这个自定义视图中,有一个图像占据了整个视图。带有此自定义视图的NSMenuItem 是菜单中的第一个,但问题是它与菜单顶部不齐,您可以在此处看到一个很大的差距:

为什么会发生这种情况,我该如何阻止它?


编辑

我现在正在使用此代码,但我在InstallControlEventHandler 线上收到了EXC_BAD_ACCESS

-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    HIViewRef contentView;
    MenuRef menuRef = [statusMenu carbonMenuRef];

    HIMenuGetContentView(menuRef, kThemeMenuTypePullDown, &contentView);

    EventTypeSpec hsEventSpec[1] = {
        { kEventClassMenu, kEventMenuCreateFrameView }
    };

    InstallControlEventHandler(contentView,
                           NewEventHandlerUPP((EventHandlerProcPtr)hsMenuCreationEventHandler),
                           GetEventTypeCount(hsEventSpec),
                           hsEventSpec,
                           NULL,
                           NULL); // Get EXC_BAD_ACCESS here.
}

static OSStatus hsMenuContentEventHandler( EventHandlerCallRef caller, EventRef event, void* refcon )
{
    OSStatus  err;

    check( GetEventClass( event ) == kEventClassControl );
    check( GetEventKind( event ) == kEventControlGetFrameMetrics );

    err = CallNextEventHandler( caller, event );
    if ( err == noErr )
    {
        HIViewFrameMetrics  metrics;

        verify_noerr( GetEventParameter( event, kEventParamControlFrameMetrics, typeControlFrameMetrics, NULL,
                                        sizeof( metrics ), NULL, &metrics ) );

        metrics.top = 0;

        verify_noerr( SetEventParameter( event, kEventParamControlFrameMetrics, typeControlFrameMetrics,
                                        sizeof( metrics ), &metrics ) );
    }

    return err;
}

static OSStatus hsMenuCreationEventHandler( EventHandlerCallRef caller, EventRef event, void* refcon )
{
    OSStatus  err = eventNotHandledErr;

    if ( GetEventKind( event ) == kEventMenuCreateFrameView)
    {
        err = CallNextEventHandler( caller, event );
        if ( err == noErr )
        {
            static const EventTypeSpec  kContentEvents[] =
            {
                { kEventClassControl, kEventControlGetFrameMetrics }
            };

            HIViewRef          frame;
            HIViewRef          content;

            verify_noerr( GetEventParameter( event, kEventParamMenuFrameView, typeControlRef, NULL,
                                            sizeof( frame ), NULL, &frame ) );
            verify_noerr( HIViewFindByID( frame, kHIViewWindowContentID, &content ) );
            InstallControlEventHandler( content, hsMenuContentEventHandler, GetEventTypeCount( kContentEvents ),
                                       kContentEvents, 0, NULL );
        }
    }

    return err;
}

还要注意metrics.top = 0 行,这是应该消除顶部间隙的行。但是我不能让它工作那么远。有谁知道我为什么会在那里收到EXC_BAD_ACCESS。我已经创建并分配了statusMenu,所以它应该可以工作吗?

【问题讨论】:

  • 看起来每个菜单的顶部和底部都有一个空格。我也想知道是否可以避免。
  • 我假设黑色部分是图像,而不是间隙?出于美学原因,除了分隔项之间,菜单的顶部和底部之间还有一些填充。我不确定这样做是 NSMenu 还是 NSMenuItem,但您可能需要将其中一个子类化以防止它发生。
  • 我做了一些研究,发现了这个 mail-archive.com/cocoa-dev@lists.apple.com/msg26997.html 看起来需要一个自定义的 NSMenu 和一些私有 API 修补。
  • 这很有趣,但是您如何将最高指标设置为零,我想知道代码会是什么样子,因为我假设它是 Carbon。
  • Joshua :我通过将“InstallControlEventHandler”函数调用替换为“HIViewInstallEventHandler”来使上述代码工作。让我知道是否有帮助。

标签: objective-c cocoa macos nsmenu nsmenuitem


【解决方案1】:

您的帖子被标记为“Objective-C”和“Cocoa”,尽管您的示例代码是 C 和 Carbon。我假设您更喜欢 Cocoa 解决方案?

在 Cocoa 中实际上非常简单。唯一的窍门是学习如何画线外。 :-)

@interface FullMenuItemView : NSView
@end

@implementation FullMenuItemView
- (void) drawRect:(NSRect)dirtyRect
{
    NSRect fullBounds = [self bounds];
    fullBounds.size.height += 4;
    [[NSBezierPath bezierPathWithRect:fullBounds] setClip];

    // Then do your drawing, for example...
    [[NSColor blueColor] set];
    NSRectFill( fullBounds );
}
@end

像这样使用它:

CGFloat menuItemHeight = 32;

NSRect viewRect = NSMakeRect(0, 0, /* width autoresizes */ 1, menuItemHeight);
NSView *menuItemView = [[[FullMenuItemView alloc] initWithFrame:viewRect] autorelease];
menuItemView.autoresizingMask = NSViewWidthSizable;

yourMenuItem.view = menuItemView;

【讨论】:

  • 效果很好!刚刚在您的代码中发现了一个错字,尽管您说 initWithRect: 应该是 initWithFrame:。非常感谢,我从来没有想过会这么简单!
  • joshua: 如果在没有 EXC_BAD_ACCESS 的情况下仍然需要上述代码,请查看stackoverflow.com/questions/6633843/…
  • @AmitSri 这个解决方案通过不使用 Carbon 完全避免了这个问题。如果不需要,为什么要使用 Carbon?
  • 不在乎 AmitSri 做了什么,但要为其他人澄清...setClip 是公开的并受支持。 Apple 的唯一警告是,如果您使用它,视图可能会超出其界限,这正是我们正在尝试做的事情。 相比之下,AmitSri 引用的 Carbon 代码已弃用 (HIMenuGetContentView)和私有 (_NSGetCarbonMenu) API。
  • 你不应该使用蓝色——有些人(比如我)使用石墨配色方案。 (: 最好使用[NSColor selectedMenuItemColor],它应该为你选择合适的颜色。
猜你喜欢
  • 2011-10-01
  • 2014-02-27
  • 1970-01-01
  • 2015-01-15
  • 2011-08-10
  • 2011-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多