我想将上下文菜单放在NSTableView
. 这部分完成了。我想做的是根据右键单击单元格的内容显示不同的菜单条目,并且不显示特定列的上下文菜单。
这是:
第 0 列和第 1 列没有上下文菜单
所有其他单元格应该有这样的上下文菜单:
第一项:“删除”samerow.column1.value
第二项:“保存”samecolumn.headertext
-编辑-
右边的一个是上下文菜单对于任何给定单元格的外观。
我想将上下文菜单放在NSTableView
. 这部分完成了。我想做的是根据右键单击单元格的内容显示不同的菜单条目,并且不显示特定列的上下文菜单。
这是:
第 0 列和第 1 列没有上下文菜单
所有其他单元格应该有这样的上下文菜单:
第一项:“删除”samerow.column1.value
第二项:“保存”samecolumn.headertext
-编辑-
右边的一个是上下文菜单对于任何给定单元格的外观。
有一个代表!- 无需子类
在 IB 中,如果您将一个NSTableView
拖到您的窗口/视图上,您会注意到menu
表格有一个出口。
所以实现上下文菜单的一个非常简单的方法是将该出口连接到存根菜单并将菜单的委托出口连接到实现NSMenuDelegate
协议方法的对象- (void)menuNeedsUpdate:(NSMenu *)menu
通常,菜单的委托是为表提供数据源/委托的同一对象,但它也可能是拥有表的视图控制器。
查看文档以获取有关此的更多信息
你可以在协议中做很多聪明的事情,但一个非常简单的实现可能如下所示
#pragma mark tableview menu delegates
- (void)menuNeedsUpdate:(NSMenu *)menu
{
NSInteger clickedrow = [mytable clickedRow];
NSInteger clickedcol = [mytable clickedColumn];
if (clickedrow > -1 && clickedcol > -1) {
//construct a menu based on column and row
NSMenu *newmenu = [self constructMenuForRow:clickedrow andColumn:clickedcol];
//strip all the existing stuff
[menu removeAllItems];
//then repopulate with the menu that you just created
NSArray *itemarr = [NSArray arrayWithArray:[newmenu itemArray]];
for(NSMenuItem *item in itemarr)
{
[newmenu removeItem:[item retain]];
[menu addItem:item];
[item release];
}
}
}
然后是构建菜单的方法。
-(NSMenu *)constructMenuForRow:(int)row andColumn:(int)col
{
NSMenu *contextMenu = [[[NSMenu alloc] initWithTitle:@"Context"] autorelease];
NSString *title1 = [NSString stringWithFormat:@"Delete %@",[self titleForRow:row]];
NSMenuItem *item1 = [[[NSMenuItem alloc] initWithTitle:title1 action:@selector(deleteObject:) keyEquivalent:@""] autorelease];
[contextMenu addItem:item1];
//
NSString *title2 = [NSString stringWithFormat:@"Save %@",[self titleForColumn:col]];
NSMenuItem *item2 = [[[NSMenuItem alloc] initWithTitle:title1 action:@selector(saveObject:) keyEquivalent:@""] autorelease];
[contextMenu addItem:item2];
return contextMenu;
}
您选择如何实施titleForRow:
,titleForColumn:
取决于您。
请注意,它NSMenuItem
提供了representedObject
允许您将任意对象绑定到菜单项的属性,从而将信息发送到您的方法(例如deleteObject:
)
编辑
注意 -- (void)menuNeedsUpdate:(NSMenu *)menu
在您的NSDocument
子类中实现将停止出现在 10.8 中的标题栏中的 Autosave/Versions 菜单。
它仍然可以在 10.7 中使用,所以请看图。在任何情况下,菜单委托都需要不是您的NSDocument
子类。
编辑:比以下方法更好的方法是使用委托,如接受的答案所示。
你可以继承你的 UITableView 并实现menuForEvent:
方法:
-(NSMenu *)menuForEvent:(NSEvent *)event{
if (event.type==NSRightMouseDown) {
if (self.selectedColumn == 0 || self.selectedColumn ==1) {
return nil;
}else {
//create NSMenu programmatically or get a IBOutlet from one created in IB
NSMenu *menu=[[NSMenu alloc] initWithTitle:@"Custom"];
//code to set the menu items
//Instead of the following line get the value from your datasource array/dictionary
//I used this as I don't know how you have implemented your datasource, but this will also work
NSString *deleteValue = [[self preparedCellAtColumn:1 row:self.selectedRow] title];
NSString *deleteString = [NSString stringWithFormat:@"Delete %@",deleteValue];
NSMenuItem *deleteItem = [[NSMenuItem alloc] initWithTitle:deleteString action:@selector(deleteAction:) keyEquivalent:@""];
[menu addItem:deleteItem];
//save item
//similarly
[menu addItem:saveItem];
return menu;
}
}
return nil;
}
那应该这样做。我还没有尝试过代码。但这应该给你一个想法。
我还尝试了 Warren Burton 发布的解决方案,效果很好。但就我而言,我必须在菜单项中添加以下内容:
[item1 setTarget:self];
[item2 setTarget:self];
不明确设置目标会导致上下文菜单保持禁用状态。
干杯!
亚历克斯
PS:我会将此作为评论发布,但我没有足够的声誉来做到这一点:(
沃伦伯顿的答案是正确的。对于那些在 Swift 中工作的人,下面的示例片段可能会为您节省从 Objective C 翻译的工作。在我的例子中,我将上下文菜单添加到 NSOutlineView 而不是 NSTableView 中的单元格。在此示例中,菜单构造函数查看项目并根据项目类型和状态提供不同的选项。委托(在 IB 中设置)是管理 NSOutlineView 的 ViewController。
func menuNeedsUpdate(menu: NSMenu) {
// get the row/column from the NSTableView (or a subclasse, as here, an NSOutlineView)
let row = outlineView.clickedRow
let col = outlineView.clickedColumn
if row < 0 || col < 0 {
return
}
let newItems = constructMenuForRow(row, andColumn: col)
menu.removeAllItems()
for item in newItems {
menu.addItem(item)
// target this object for handling the actions
item.target = self
}
}
func constructMenuForRow(row: Int, andColumn column: Int) -> [NSMenuItem]
{
let menuItemSeparator = NSMenuItem.separatorItem()
let menuItemRefresh = NSMenuItem(title: "Refresh", action: #selector(refresh), keyEquivalent: "")
let item = outlineView.itemAtRow(row)
if let block = item as? Block {
let menuItem1 = NSMenuItem(title: "Delete \(block.name)", action: #selector(deleteBlock), keyEquivalent: "")
let menuItem2 = NSMenuItem(title: "New List", action: #selector(addList), keyEquivalent: "")
return [menuItem1, menuItem2, menuItemSeparator, menuItemRefresh]
}
if let field = item as? Field {
let menuItem1 = NSMenuItem(title: "Delete \(field.name)", action: #selector(deleteField), keyEquivalent: "")
let menuItem2 = NSMenuItem(title: "New Field", action: #selector(addField), keyEquivalent: "")
return [menuItem1, menuItem2, menuItemSeparator, menuItemRefresh]
}
return [NSMenuItem]()
}
正如 TheGoonie 提到的,我也得到了相同的体验——上下文菜单项仍处于禁用状态。但是,禁用项目的原因是“自动启用项目”属性。
关闭“自动启用项目”属性。或以编程方式将其设置为 NO。
[mTableViewMenu setAutoenablesItems:NO];
这是一个在视图控制器中以编程方式设置 NSOutlineView 的示例。这是启动和运行上下文菜单所需的所有管道。不需要子类化。
我之前曾将 NSOutlineView 子类化以覆盖菜单(对于事件:NSEvent),但在 Graham 的回答和 Warren 的回答的帮助下进行了更简单的设置。
class OutlineViewController: NSViewController
{
// ...
var outlineView: NSOutlineView!
var contextMenu: NSMenu!
override func viewDidLoad()
{
// ...
outlineView = NSOutlineView()
contextMenu = NSMenu()
contextMenu.delegate = self
outlineView.menu = contextMenu
}
}
extension OutlineViewController: NSMenuDelegate
{
func menuNeedsUpdate(_ menu: NSMenu) {
// clickedRow catches the right-click here
print("menuNeedsUpdate called. Clicked Row: \(outlineView.clickedRow)")
// ... Flesh out the context menu here
}
}
NSMenu
这是我发现的自定义/动态最简单的方法,它还保留了系统外观(蓝色选择边框)。子类化NSTableView
并将您的菜单设置在menu(for:)
.
重要的部分是在表视图上设置菜单,但从它的调用中返回菜单super
。
override func menu(for event: NSEvent) -> NSMenu? {
let point = convert(event.locationInWindow, from: nil)
let clickedRow = self.row(at: point)
var menuRows = selectedRowIndexes
// The blue selection box should always reflect the
// returned row indexes.
if menuRows.isEmpty || !menuRows.contains(clickedRow) {
menuRows = [clickedRow]
}
// Build your custom menu based on the menuRows indexes
self.menu = <#myMenu#>
return super.menu(for: event)
}