2014-09-30 19 views
5

Nella mia app OSX ho una vista raccolta che è una sottoclasse di NSCollectionView.Come implementare il menu contestuale per NSCollectionView

Sono contento di come sono le cose tranne il menu contestuale, che non riesco ancora a capire.

Quindi quello che voglio è:

  • destro del mouse su un elemento di vista collezione si apre il menu contestuale
  • le opzioni scelto dal menu (eliminare, modificare, ecc) vengono applicate alla voce che il clic è stato eseguito su.

So come farlo per NSOutlineView o NSTableView, ma non per la vista di raccolta.

Non riesco a capire come ottenere l'indice dell'elemento su cui si fa clic.

Qualcuno ha qualche idea su come posso implementarlo?

Qualsiasi tipo di aiuto è molto apprezzato!

risposta

4

Un approccio che ho utilizzato è di non provare ad applicare le azioni del menu contestuale all'elemento specifico su cui è stato fatto clic ma agli elementi selezionati. E faccio in modo che l'elemento cliccato si aggiunga alla selezione.

Ho usato una vista personalizzata per la vista della collezione. La classe di visualizzazione personalizzata ha una presa, item, nell'elemento di visualizzazione raccolta proprietario, che connetto in NIB. Esso sovrascrive anche -rightMouseDown: di avere l'elemento stesso aggiungere alla selezione:

- (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]; 
} 

Se si preferisce, invece di aggiungere l'articolo alla selezione, è possibile controllare se è già nella selezione. Se lo è, non modificare la selezione. In caso contrario, sostituire la selezione con solo l'elemento (rendendolo l'unico elemento selezionato).

In alternativa, è possibile impostare un menu contestuale sulle viste di articolo anziché sulla vista di raccolta. Quindi, le voci del menu potrebbero scegliere come target la vista oggetto o la vista della raccolta.

Infine, è possibile creare sottoclasse NSCollectionView e ignorare -menuForEvent:. Dovresti comunque chiamare fino al super e restituire il menu restituito, ma potresti cogliere l'opportunità per registrare l'evento e/o l'articolo nella sua posizione. Per determinare che, devi fare qualcosa di simile:

- (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

bellissimo !!! Grazie mille! –

+1

Questo è troppo complicato. Hai provato a impostare l'uscita menu della tua vista raccolta su un menu dal file pennino? Imposta l'oggetto controller come delegato per il menu. Ogni volta che si richiama il menu, aggiornare il menu in base alla selezione nella vista raccolta. – iljawascoding

+0

Mi piace molto l'approccio sottoclasse 'NSCollectionView' - fornisce una buona coerenza di comportamento con' NSTableView'. –

3

Ecco Ken's idea ignorare menuForEvent: in un NSCollectionView sottoclasse implementato in Swift:

// 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

Grazie per questa soluzione. Ho avvolto in una sottoclasse NSCollectionView:

#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 

modo da poter utilizzare attacchi in Interface Builder per evidenziare le voci cliccato pure.

#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

In sostanza, tutte le nostre soluzioni sono in grado di affrontare i requisiti, ma mi piacerebbe fare un supplemento al swift3 +, che credo sia una soluzione completa.

/// 扩展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? 
} 

Questo è quello che ho scritto un'estensione e spero di aiutare tutti.

Problemi correlati