2014-09-30 5 views
5

In meiner OSX App habe ich eine Sammlungsansicht, die eine Unterklasse von NSCollectionView ist.So implementieren Sie das Kontextmenü für NSCollectionView

Ich bin alle zufrieden mit dem, was die Dinge sind, außer dem Kontextmenü, das ich noch nicht herausfinden kann.

Also, was ich will, ist:

  • der rechten Maustaste auf eine Sammlung Ansicht Artikel bringt das Kontextmenü
  • die Optionen aus dem Menü gepflückt (löschen, bearbeiten, usw.) auf das Element angewendet werden dass der Klick ausgeführt wurde.

Ich weiß, wie es für NSOutlineView oder NSTableView zu tun, aber nicht für die Sammlungsansicht.

Ich kann nicht herausfinden, wie man den Index des Artikels geklickt bekommen.

Hat jemand irgendwelche Ideen, wie ich das umsetzen kann?

Jede Art von Hilfe wird sehr geschätzt!

Antwort

4

Ein Ansatz, den ich verwendet habe, besteht darin, nicht zu versuchen, die kontextabhängigen Menüaktionen auf das eine bestimmte Element, auf das geklickt wurde, sondern auf die ausgewählten Elemente anzuwenden. Und ich mache das angeklickte Element zur Auswahl hinzufügen.

Ich verwendete eine benutzerdefinierte Ansicht für die Sammlungselementansicht. Die benutzerdefinierte Ansichtsklasse hat einen Ausgang item zu seinem besitzenden Auflistungsansichtselement, das ich in der NIB verbinde. Er überschreibt auch -rightMouseDown: das Element zu haben, sich auf die Auswahl hinzufügen:

- (void) rightMouseDown:(NSEvent*)event 
{ 
    NSCollectionView* parent = self.item.collectionView; 
    NSUInteger index = NSNotFound; 
    NSUInteger count = parent.content.count; 
    for (NSUInteger i = 0; i < count; i++) 
    { 
     if ([parent itemAtIndex:i] == self.item) 
     { 
      index = i; 
      break; 
     } 
    } 

    NSMutableIndexSet* selectionIndexes = [[parent.selectionIndexes mutableCopy] autorelease]; 
    if (index != NSNotFound && ![selectionIndexes containsIndex:index]) 
    { 
     [selectionIndexes addIndex:index]; 
     parent.selectionIndexes = selectionIndexes; 
    } 

    return [super rightMouseDown:event]; 
} 

Wenn Sie es vorziehen, anstatt den Artikel der Auswahl hinzufügen, können Sie überprüfen, ob es bereits bei der Auswahl ist. Wenn dies der Fall ist, ändern Sie die Auswahl nicht. Wenn es nicht ist, ersetzen Sie die Auswahl nur mit dem Element (wodurch es das einzige ausgewählte Element).

Alternativ können Sie ein Kontextmenü für die Objektansichten und nicht für die Sammlungsansicht festlegen. Dann können die Menüelemente entweder auf die Elementansicht oder auf das Objekt der Sammlungsansicht ausgerichtet werden.

Zuletzt könnten Sie die Unterklasse und überschreiben -menuForEvent:. Sie würden immer noch durch super aufrufen und das Menü zurückkehren, das es zurückgibt, aber Sie könnten die Gelegenheit nutzen, um das Ereignis und/oder den Artikel an seinem Standort aufzuzeichnen. Um festzustellen, dass, würden tun Sie so etwas wie:

- (NSMenu*) menuForEvent:(NSEvent*)event 
{ 
    _clickedItemIndex = NSNotFound; 
    NSPoint point = [self convertPoint:event.locationInWindow fromView:nil]; 
    NSUInteger count = self.content.count; 
    for (NSUInteger i = 0; i < count; i++) 
    { 
     NSRect itemFrame = [self frameForItemAtIndex:i]; 
     if (NSMouseInRect(point, itemFrame, self.isFlipped)) 
     { 
      _clickedItemIndex = i; 
      break; 
     } 
    } 

    return [super menuForEvent:event]; 
} 
+0

schön !!! Ich danke dir sehr! –

+1

Das ist viel zu kompliziert. Haben Sie versucht, den Menüausgang Ihrer Sammlungsansicht auf ein Menü aus Ihrer NIB-Datei zu setzen? Legen Sie Ihr Controller-Objekt als Delegat für das Menü fest. Jedes Mal, wenn das Menü aufgerufen wird, aktualisieren Sie Ihr Menü entsprechend der Auswahl in der Sammlungsansicht. – iljawascoding

+0

Ich mag wirklich die 'NSCollectionView'-Unterklasse Ansatz - gibt eine gute Konsistenz des Verhaltens mit' NSTableView'. –

3

Ken's idea Hier menuForEvent: in einer NSCollectionView Unterklasse in Swift implementiert außer Kraft zu setzen:

// MARK: - Properties 

/** 
The index of the item the user clicked. 
*/ 
var clickedItemIndex: Int = NSNotFound 

// MARK: - Menu override methods 

override func menuForEvent(event: NSEvent) -> NSMenu? 
{ 
    self.clickedItemIndex = NSNotFound 

    let point = self.convertPoint(event.locationInWindow, fromView:nil) 
    let count = self.content.count 

    for index in 0 ..< count 
    { 
     let itemFrame = self.frameForItemAtIndex(index) 
     if NSMouseInRect(point, itemFrame, self.flipped) 
     { 
      self.clickedItemIndex = index 
      break 
     } 
    } 

    return super.menuForEvent(event) 
} 
0

Dank für diese Lösung. Ich wickelte sie in eine NSCollectionView Unterklasse:

#import <Cocoa/Cocoa.h> 

@interface TAClickableCollectionViewItem : NSCollectionViewItem 
@property (nonatomic, assign) BOOL isClicked; 
@end 



@interface TAClickableCollectionView : NSCollectionView <NSMenuDelegate> 
@property (nonatomic, readonly) id clickedObject; 
@property (nonatomic, readonly) TAClickableCollectionViewItem *clickedItem; 
@end 

So können Sie Bindungen in Interface Builder verwenden, um auch angeklickt Elemente zu markieren.

#import "TAClickableCollectionView.h" 

@implementation TAClickableCollectionViewItem 
@end 



@implementation TAClickableCollectionView 

- (NSMenu*) menuForEvent:(NSEvent*)event 
{ 
    NSInteger _clickedItemIndex = NSNotFound; 
    NSPoint point = [self convertPoint:event.locationInWindow fromView:nil]; 
    NSUInteger count = self.content.count; 
    for (NSUInteger i = 0; i < count; i++) 
    { 


    NSRect itemFrame = [self frameForItemAtIndex:i]; 
     if (NSMouseInRect(point, itemFrame, self.isFlipped)) 
      { 
      _clickedItemIndex = i; 
      break; 
      } 
     } 

    if(_clickedItemIndex < self.content.count) { 
     id obj = [self.content objectAtIndex:_clickedItemIndex]; 
     TAClickableCollectionViewItem *item = (TAClickableCollectionViewItem *)[self itemAtIndex:_clickedItemIndex]; 

     if(item != _clickedItem) { 
      [self willChangeValueForKey:@"clickedObject"]; 
      _clickedItem.isClicked = NO; 
      _clickedItem = item; 
      [self didChangeValueForKey:@"clickedObject"]; 
     } 

     item.isClicked = YES; 

     if(obj != _clickedObject) { 
      [self willChangeValueForKey:@"clickedObject"]; 
      _clickedObject = obj; 
      [self didChangeValueForKey:@"clickedObject"]; 
     } 
    } 

    return [super menuForEvent:event]; 
} 

- (void)menuDidClose:(NSMenu *)menu { 
    _clickedItem.isClicked = NO; 
} 
@end 
1

Grundsätzlich sind alle unsere Lösungen sind in der Lage Anforderungen der Adressierung, aber ich möchte eine Ergänzung zu swift3 + machen, was ich denke, ist eine komplette Lösung.

/// 扩展NSCollectionView功能,增加常用委托 
class ANCollectionView: NSCollectionView { 
    // 扩展委托方式 
    weak open var ANDelegate: ANCollectionViewDelegate? 

    override func menu(for event: NSEvent) -> NSMenu? { 
     var menu = super.menu(for: event); 
     let point = self.convert(event.locationInWindow, from: nil) 
     let indexPath = self.indexPathForItem(at: point); 
     if ANDelegate != nil{ 
      menu = ANDelegate?.collectionView(self, menu: menu, at: indexPath); 
     } 
     return menu; 
    } 
} 

/// 扩展NSCollectionView的委托 
protocol ANCollectionViewDelegate : NSObjectProtocol { 
    func collectionView(_ collectionView:NSCollectionView, menu:NSMenu?, at indexPath: IndexPath?) -> NSMenu? 
} 

Dies ist, was ich eine Erweiterung geschrieben habe, und ich hoffe, jeder zu helfen.