我想将一个上下文菜单放到NSTableView
上。这部分完成了。我要做的就是根据右键单元格的内容显示不同的菜单条目,不要显示特定列的上下文菜单。
这是:
第0列,1没有上下文菜单
所有其他单元格应该具有如下的上下文菜单:
第一个条目:“删除”samerow.column1.value
第二个条目:“保存”samecolumn.headertext
希望问题很明确。
感谢
- 编辑 -
右边的那个是上下文菜单对于任何给定单元格应该是什么样的。
答案 0 :(得分:35)
Theres代表那个! - 无需子类
在IB中,如果您将NSTableView
拖到窗口/视图上,您会注意到该表的menu
出口。
因此,实现上下文菜单的一种非常简单的方法是将该出口连接到存根菜单,并将菜单的委托出口连接到实现NSMenuDelegate
协议方法- (void)menuNeedsUpdate:(NSMenu *)menu
的对象
通常,菜单的委托是向表提供数据源/委托的同一对象,但它也可能是拥有该表的视图控制器。
Have a look at the docs了解有关此内容的更多信息
你可以在协议中使用一堆聪明的东西,但是一个非常简单的实现可能如下所示
#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中出现的标题栏中的自动保存/版本菜单。
它仍然适用于10.7所以去图。在任何情况下,菜单委托都必须是您的NSDocument
子类以外的其他东西。
答案 1 :(得分:3)
编辑:比以下方法更好的方法是使用委托,如接受的答案所示。
您可以创建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;
}
应该这样做。我没有尝试过代码。但这应该会给你一个想法。
答案 2 :(得分:2)
我也尝试过Warren Burton发布的解决方案,它运行正常。 但在我的情况下,我不得不在菜单项中添加以下内容:
[item1 setTarget:self];
[item2 setTarget:self];
明确设置无目标会导致上下文菜单保持禁用状态。
干杯!
亚历
PS:我会将此作为评论发布,但我没有足够的声誉:
答案 3 :(得分:1)
Warren Burton的回答是正确的。对于那些在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]()
}
答案 4 :(得分:0)
正如TheGoonie所提到的,我也得到了相同的经验 - 上下文菜单项仍然被禁用。但是,项目被禁用的原因是“自动启用项目”&#39;属性。
制作&#39;自动启用商品&#39;财产关闭。或以编程方式将其设置为NO。
[mTableViewMenu setAutoenablesItems:NO];
答案 5 :(得分:0)
以下是在视图控制器中以编程方式设置NSOutlineView的示例。这是启动和运行上下文菜单所需的所有管道。不需要子类化。
我之前已经将NSOutlineView子类化为覆盖菜单(对于事件:NSEvent),但在上面的Graham's回答here和Warren's回答的帮助下进行了更简单的设置。
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
}
}
答案 6 :(得分:0)
这是我发现的自定义/动态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)
}