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
Stored Variable of Self Type (Especially When Subclassing)
Borders Not Covering Background
How to Completely Remove All Xcode Program and Cache Files
Check If Class Has a Value for a Key
Swift Type Inference and Type Checking Issue
Testing a Timer in Xcode with Xctest
Swift Unable to Locate and Read Property List (.Plist) File
Error Loading Media Sources in Mlmedialibrary
Test Enum for Equality in for ... Where Clause
Swiftui: What Are The Differences Between Image and UIimage
Extra Argument Userinfo in Call
Nscollectionview Selection Handling in Swift
How to Test Whether an Array's Type Is Optional
How to Mirror The Design of The Codable/Codablekeys Protocols
Finding The First Non-Repeating Character in a String Using Swift
Missing Required Module Firebase - Jenkins Build Error
How to Byte Reverse Nsdata Output in Swift The Littleendian Way