6

ソースリスト(NSOutlineView)のあるウィンドウがあります。私のソースリストには2つのレベルしかありません。レベル1はヘッダーで、レベル2はデータです。一部のデータセルにコンテキストメニューが必要です。すべてではない。

まず、データセルを表すメニューをテーブルセルビューにアタッチしようとします->何も起こりません。

次に、IBのアウトラインビューにメニューを添付します->各セル(ヘッダーとデータ)でコンテキストメニューが開きます。メニューの表示を停止するように検索しましたが、何も見つかりません。

アイデアはありますか?

ありがとうございました

OS X 10.8.2 Lion、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プロパティです。

(以下はSwiftです)

// 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:があることを確認し、ある場合は呼び出されます。そうでない場合、それは終了し、他に何も起こりません。これが、テーブルビューがrightMouseDown:を飲み込むため、NSTableCellViewがmenuForEvent:に応答しない理由です。

テーブルビューをサブクラス化し、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 に答える