Nscollectionview Selection Handling in Swift

Selection Highlight in NSCollectionView

Its not too hard to do. Make sure "Selection" is enabled for the NSCollectionView in Interface Builder. Then in the NSView subclass that you are using for your prototype view, declare a property called "selected" :

@property (readwrite) BOOL selected;

UPDATED CODE HERE: (added super call)

Subclass NSCollectionViewItem and override -setSelected:

- (void)setSelected:(BOOL)flag
{
[super setSelected:flag];
[(PrototypeView*)[self view] setSelected:flag];
[(PrototypeView*)[self view] setNeedsDisplay:YES];
}

Then you need to add code in your prototype view's drawRect: method to draw the highlight:

- (void)drawRect:(NSRect)dirtyRect 
{
if (selected) {
[[NSColor blueColor] set];
NSRectFill([self bounds]);
}
}

That just simply fills the view in blue when its selected, but that can be customized to draw the highlight any way you want. I've used this in my own apps and it works great.

NSCollectionView selection and deselection

Apple has a WWDC session in 2015 talking about What's New in NSCollectionView with the new API. It talks about Selection at 36'33".

The Objective-C Sample Code CocoaSlideCollection shows the selection code in action.

I created a video tutorial on Selection as well, Sample code is available here.

How to properly show the current selection in an NSCollectionView?

The problem turned out to be calling [collectionView reloadItemsAtIndexPaths:]. When you do that, it removes the existing NSCollectionViewItem and creates a new one (by calling your data source's collectionView:itemForRepresentedObjectAt:). That immediately sets the new collection view item to not selected (or rather it doesn't set it to be selected). When that happens, it won't call your should/didDeselect methods because the existing item doesn't exist anymore, and the new one is not selected.

The real solution turned out to be to subclass NSCollectionViewItem and override -setSelected: to do the following:

- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
[self.view setNeedsDisplay:YES];
}

When the view's -drawRect: method gets called, it asks the item if it's selected and draws appropriately.

Therefore, I could completely remove all of the should/did/select/Deselect methods from the delegate without any problem, and it all just worked!

Changing the selection behaviour of NSCollectionView

You could try intercepting all left-mouse-down events using a local events monitor. Within this block you'd then work out if the click happened on your collection view. If it did, create a new event which mimics the event you intercepted but add in the command key mask if it isn't already present. Then, at the end of the block return your event rather than the one you intercepted. Your collection view will behave as if the user had pressed the command key, even though they haven't!

I had a quick go with this in a very simple demo app and it looks like a promising approach - though I expect you'll have to negotiate a few gotchas along the way.

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskFromType(NSLeftMouseDown)
handler:^NSEvent *(NSEvent *originalEvent) {

// Did this left down event occur on your collection view?
// If it did add in the command key

NSEvent *newEvent =
[NSEvent
mouseEventWithType: NSLeftMouseDown
location: originalEvent.locationInWindow
modifierFlags: NSCommandKeyMask // I'm assuming it's not already present
timestamp: originalEvent.timestamp
windowNumber: originalEvent.windowNumber
context: originalEvent.context
eventNumber: originalEvent.eventNumber
clickCount: originalEvent.clickCount
pressure:0];

return newEvent; // or originalEvent if it's nothing to do with your collection view
}];
}

Edit (by question author):

This solution is so heavily based on the original answer that this answer deserves credit (feel free to edit)

You can also intercept the mouse event by subclassing the NSCollectionView class and overriding mousedown like this:

@implementation MyCollectionView

-(void) mouseDown:(NSEvent *)originalEvent {

NSEvent *mouseEventWithCmd =
[NSEvent
mouseEventWithType: originalEvent.type
location: originalEvent.locationInWindow
modifierFlags: NSCommandKeyMask
timestamp: originalEvent.timestamp
windowNumber: originalEvent.windowNumber
context: originalEvent.context
eventNumber: originalEvent.eventNumber
clickCount: originalEvent.clickCount
pressure: originalEvent.pressure];

[super mouseDown: mouseEventWithCmd];
}

@end

Move selection sequentially in NSCollectionView

I have asked same question to Apple Developer Technical Support, after checked they said "Our engineers have reviewed your request and have determined that this would be best handled as a bug report.". I submitted bug report to Apple's radar. So far no response.

I decided to implement my own solution. Subclass your NSCollectionView and override keyDown event.

Swift 4

override func keyDown(with event: NSEvent) {
if event.modifierFlags.rawValue == 10617090 {
return
}
if event.isARepeat == true && event.keyCode != 123 && event.keyCode != 124 && event.keyCode != 125 && event.keyCode != 126 {
return
}
if event.keyCode == 123 || event.keyCode == 124 || event.keyCode == 125 || event.keyCode == 126 {
for index in self.selectionIndexes {
if event.keyCode == 124 && index < YOUR_DATASOURCE_ARRAY.count - 1 {
self.deselectItems(at: [NSIndexPath(forItem: index, inSection: 0) as IndexPath])
self.selectItems(at: [NSIndexPath(forItem: index + 1, inSection: 0) as IndexPath], scrollPosition: NSCollectionView.ScrollPosition.nearestHorizontalEdge)
return
}
if event.keyCode == 123 && index > 0 {
self.deselectItems(at: [NSIndexPath(forItem: index, inSection: 0) as IndexPath])
self.selectItems(at: [NSIndexPath(forItem: index - 1, inSection: 0) as IndexPath], scrollPosition: NSCollectionView.ScrollPosition.nearestHorizontalEdge)
return
}
}
}
super.keyDown(with: event)
}

This interrupts left and right arrow key inputs and move selection to previous/next cell. Since
"Allows Multiple Selection" has to be NO. Also disable modifier keys as like CMD or Control.

If you keep pressing right arrow, when selection arrives to last cell at right hand it jump one row down and keep moving or vice versa for left arrow.
Hope it helps.



Related Topics



Leave a reply



Submit