6

我有一个带有源列表(NSOutlineView)的窗口。我的源列表只有两个级别。一级是标题,二级是数据。我想在某些数据单元格上有一个上下文菜单。不是全部。

首先,我尝试在代表数据单元格的表格单元格视图上附加一个菜单-> 没有任何反应。

其次,我在 IB 的大纲视图上附加了一个菜单 -> 在每个单元格(标题和数据)上打开上下文菜单。我搜索停止打开菜单,但我什么也没找到。

你有什么想法吗?

谢谢

OS X 10.8.2 狮子,Xcode 4.5.2,SDK 10.8

4

4 回答 4

7

如果你继承 NSOutlineView,你可以重写menuForEvent:以仅在用户单击正确的行时返回菜单。这是一个例子:

- (NSMenu *)menuForEvent:(NSEvent *)event;
{
    //The event has the mouse location in window space; convert it to our (the outline view's) space so we can find which row the user clicked on.
    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
    NSInteger row = [self rowAtPoint:point];

    //If the user did not click on a row, or is not exactly one level down from the top level of hierarchy, return nil—that is, no menu.
    if ( row == -1 || [self levelForRow:row] != 1 )
        return nil;

    //Create and populate a menu.
    NSMenu *menu = [[NSMenu alloc] init];
    NSMenuItem *delete = [menu addItemWithTitle:NSLocalizedString( @"Delete", @"" ) action:@selector(delete:) keyEquivalent:@""];

    [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];

    //Set the Delete menu item's represented object to the clicked-on item. If the user chooses this item, we'll retrieve its represented object so we know what to delete.
    [delete setRepresentedObject:[self itemAtRow:row]];

    return menu;
}

这假设我们正在使用 ARC 进行编译,因此您不需要自动释放正在创建的菜单对象。

于 2013-02-10T20:18:49.397 回答
2

这个扩展 + 子类(包括 NSOutlineView 和 NSTableView)可以明智地查看菜单是否附加到单元格视图或行视图。只是一个通用的、可重用的子类!

在单元格视图中设置菜单outlineView:viewForTableColumn:item:-menu是一个 NSResponder 属性。

(下面是斯威夫特)

// An extension lets us both subclass NSTableView and NSOutlineView with the same functionality
extension NSTableView {
    // Find a cell view, or a row view, that has a menu. (e.g. NSResponder’s menu: NSMenu?)
    func burnt_menuForEventFromCellOrRowViews(event: NSEvent) -> NSMenu? {
        let point = convertPoint(event.locationInWindow, fromView: nil)
        let row = rowAtPoint(point)
        if row != -1 {
            if let rowView = rowViewAtRow(row, makeIfNecessary: true) as? NSTableRowView {
                let column = columnAtPoint(point)
                if column != -1 {
                    if let cellView = rowView.viewAtColumn(column) as? NSTableCellView {
                        if let cellMenu = cellView.menuForEvent(event) {
                            return cellMenu
                        }
                    }
                }

                if let rowMenu = rowView.menuForEvent(event) {
                    return rowMenu
                }
            }
        }

        return nil
    }
}


class OutlineView: NSOutlineView {
    override func menuForEvent(event: NSEvent) -> NSMenu? {
        // Because of weird NSTableView/NSOutlineView behaviour, must set receiver’s menu otherwise the target cannot be found
        self.menu = burnt_menuForEventFromCellOrRowViews(event)

        return super.menuForEvent(event)
    }
}

class TableView: NSTableView {
    override func menuForEvent(event: NSEvent) -> NSMenu? {
        // Because of weird NSTableView/NSOutlineView behaviour, must set receiver’s menu otherwise the target cannot be found
        self.menu = burnt_menuForEventFromCellOrRowViews(event)

        return super.menuForEvent(event)
    }
}
于 2015-05-03T14:56:02.480 回答
0

从您的问题中不清楚您的大纲是基于视图还是基于单元格。这很重要。

如果您是基于视图的,那么您的视图实例可以实现

- (NSMenu *)menuForEvent:(NSEvent *)theEvent

并返回适合该项目的菜单 - 如果您根本不想要菜单,则返回 nil 。

如果您是基于单元格的,或者出于某种原因不想在视图类中处理此问题,则需要将 NSOutlineView 子类化并- (NSMenu *)menuForEvent:(NSEvent *)theEvent在那里实现。同样,您将确定哪个单元格被点击或激活,并从中决定您想要什么菜单。

于 2013-02-14T01:00:46.380 回答
0
- (void)rightMouseDown:(NSEvent *)event

NSView 不会将此传递给下一个视图,此方法查看当前类是否有一个 menuForEvent:,如果有,则调用它。如果没有,那么它就完成了,不会发生其他任何事情。这就是为什么您不会看到 NSTableCellView 响应 menuForEvent: 的原因,因为表格视图吞下了 rightMouseDown:。

您可以子类化 tableview 并处理 rightMouseDown: 事件并调用 NSTableCellView 的 rightMouseDown: 并处理显示您在情节提要中构建并连接到 NSTableViewCell 的菜单。

这是我在子类 NSTableView 中的解决方案:

- (void)rightMouseDown:(NSEvent *)event
{
    for (NSTableRowView *rowView in self.subviews) {

        for (NSView *tableCellView in [rowView subviews]) {

            if (tableCellView) {
                NSPoint eventPoint = [event locationInWindow];
//            NSLog(@"Window Point:     %@", NSStringFromPoint(eventPoint));

                eventPoint = [self convertPoint:eventPoint toView:nil];
                eventPoint = [self convertPoint:eventPoint toView:self];
//            NSLog(@"Table View Point: %@", NSStringFromPoint(eventPoint));

                NSRect newRect = [tableCellView convertRect:[tableCellView bounds] toView:self];
//            NSLog(@"Rect: %@", NSStringFromRect(newRect));

                BOOL rightMouseDownInTableCellView = [tableCellView mouse:eventPoint inRect:newRect];
//            NSLog(@"Mouse in view: %hhd", mouseInView);

                if (rightMouseDownInTableCellView) {
                    if (tableCellView) {
                        // Lets be safe and make sure that the object is going to respond.
                        if ([tableCellView respondsToSelector:@selector(rightMouseDown:)]) {
                            [tableCellView rightMouseDown:event];
                        }
                    }
                }
            }
        }
    }
}    

这将找到鼠标右键事件发生的位置,检查我们是否有正确的视图并将 rightMouseDown: 传递给该视图。

请让我知道此解决方案是否适合您。

于 2016-08-14T15:34:16.153 回答