我有一个带有源列表(NSOutlineView)的窗口。我的源列表只有两个级别。一级是标题,二级是数据。我想在某些数据单元格上有一个上下文菜单。不是全部。
首先,我尝试在代表数据单元格的表格单元格视图上附加一个菜单-> 没有任何反应。
其次,我在 IB 的大纲视图上附加了一个菜单 -> 在每个单元格(标题和数据)上打开上下文菜单。我搜索停止打开菜单,但我什么也没找到。
你有什么想法吗?
谢谢
OS X 10.8.2 狮子,Xcode 4.5.2,SDK 10.8
我有一个带有源列表(NSOutlineView)的窗口。我的源列表只有两个级别。一级是标题,二级是数据。我想在某些数据单元格上有一个上下文菜单。不是全部。
首先,我尝试在代表数据单元格的表格单元格视图上附加一个菜单-> 没有任何反应。
其次,我在 IB 的大纲视图上附加了一个菜单 -> 在每个单元格(标题和数据)上打开上下文菜单。我搜索停止打开菜单,但我什么也没找到。
你有什么想法吗?
谢谢
OS X 10.8.2 狮子,Xcode 4.5.2,SDK 10.8
如果你继承 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 进行编译,因此您不需要自动释放正在创建的菜单对象。
这个扩展 + 子类(包括 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)
}
}
从您的问题中不清楚您的大纲是基于视图还是基于单元格。这很重要。
如果您是基于视图的,那么您的视图实例可以实现
- (NSMenu *)menuForEvent:(NSEvent *)theEvent
并返回适合该项目的菜单 - 如果您根本不想要菜单,则返回 nil 。
如果您是基于单元格的,或者出于某种原因不想在视图类中处理此问题,则需要将 NSOutlineView 子类化并- (NSMenu *)menuForEvent:(NSEvent *)theEvent
在那里实现。同样,您将确定哪个单元格被点击或激活,并从中决定您想要什么菜单。
- (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: 传递给该视图。
请让我知道此解决方案是否适合您。