2014-09-30 8 views
5

В моем OSX app У меня есть представление коллекции, которое является подклассом NSCollectionView.Как реализовать контекстное меню для NSCollectionView

Я все доволен тем, что происходит, кроме контекстного меню, которое я пока не могу понять.

Так что я хочу:

  • правой кнопкой мыши на элементе просмотра коллекции вызывает контекстное меню
  • параметры выбираются из меню (удаление, редактирование и т.д.) применяются к элементу что клик был выполнен.

Я знаю, как это сделать для NSOutlineView или NSTableView, но не для коллекции.

Я не могу понять, как получить индекс элемента, нажатого.

Есть ли у кого-нибудь идеи о том, как я могу это реализовать?

Любая помощь очень ценится!

ответ

4

Один из подходов, который я использовал, заключается в том, чтобы не пытаться применять действия контекстного меню к одному конкретному элементу, на который было нажато, но к выбранным элементам. И я делаю элемент clicked-on, добавляющий себя к выбору.

Я использовал пользовательский вид для просмотра элемента коллекции. У пользовательского класса представления есть выход, item, его элемент просмотра коллекции, который я подключаю в NIB. Он также отменяет -rightMouseDown: иметь элемент добавить себя к выбору:

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

Если вы предпочитаете, а не добавление элемента к выбору, вы можете проверить, если он уже в отборе. Если это так, не изменяйте выбор. Если это не так, замените выбор только одним элементом (что делает его единственным выбранным элементом).

В качестве альтернативы вы можете установить контекстное меню в представлениях элементов, а не в виде коллекции. Затем пункты меню могут быть нацелены либо на элемент, либо на элемент коллекции.

Наконец, вы можете подкласса NSCollectionView и переопределить -menuForEvent:. Вы все равно вызовете до super и вернете меню, которое оно возвращает, но вы можете воспользоваться возможностью для записи события и/или элемента в его местоположении. Для того, чтобы определить, что вы могли бы сделать что-то вроде:

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

прекрасный !!! Спасибо огромное! –

+1

Это слишком сложно. Вы пробовали установить в меню меню вашего коллекционного представления меню из файла nib? Задайте свой объект контроллера в качестве делегата для меню. Каждый раз, когда вызывается меню, обновляйте свое меню в соответствии с выбором в представлении коллекции. – iljawascoding

+0

Мне очень нравится подкласс подкласса «NSCollectionView» - обеспечивает хорошую согласованность поведения с «NSTableView». –

3

Вот Ken's idea переопределить menuForEvent: в NSCollectionView подкласса, реализованного в 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

Спасибо за это решение. Я завернул его в подкласс 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 

Таким образом, вы можете использовать привязки в Interface Builder, чтобы выделить кликали элементы, а также.

#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

В принципе, все наши решения способны адресовать требования, но я хотел бы сделать дополнение к swift3 +, которые я думаю, это полное решение.

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

Это то, что я написал расширением, и я надеюсь помочь всем.