Selection Highlight in NSCollectionView

柔情痞子 提交于 2019-11-28 04:31:11

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.

If a different background color will suffice as a highlight, you could simply use an NSBox as the root item for you collection item view. Fill the NSBox with the highlight color of your choice. Set the NSBox to Custom so the fill will work. Set the NSBox to transparent.

Bind the transparency attribute of the NSBox to the selected attribute of File Owner(Collection Item) Set the value transformer for the transparent binding to NSNegateBoolean.

I tried to attach Interface builder screenshots but I was rejected bcos I'm a newbie :-(

You can also go another way, if you're not subclassing NSView for your protoype view.

In your subclassed NSCollectionViewItem override setSelected:

- (void)setSelected:(BOOL)selected
{
    [super setSelected:selected];
    if (selected)
        self.view.layer.backgroundColor = [NSColor redColor].CGColor;
    else
        self.view.layer.backgroundColor = [NSColor clearColor].CGColor;
}

And of course, as said by all the wise people before me, make sure "Selection" is enabled for the NSCollectionView in Interface Builder.

Since none of the existing answers worked super well for me, here is my take on it. Change the subclass of the CollectionView item to SelectableCollectionViewItem. Here is it's code. Comes with a bindable textColor property for hooking your text label textColor binding to.

@implementation SelectableCollectionViewItem

+ (NSSet *)keyPathsForValuesAffectingTextColor
{
    return [NSSet setWithObjects:@"selected", nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.wantsLayer = YES;
}

- (void) viewDidAppear
{
    // seems the inital selection state is not done by Apple in a KVO compliant manner, update background color manually
    [self updateBackgroundColorForSelectionState:self.isSelected];
}

- (void)updateBackgroundColorForSelectionState:(BOOL)flag
{
    if (flag)
    {
        self.view.layer.backgroundColor = [[NSColor alternateSelectedControlColor] CGColor];
    }
    else
    {
        self.view.layer.backgroundColor = [[NSColor clearColor] CGColor];
    }
}

- (void)setSelected:(BOOL)flag
{
    [super setSelected:flag];
    [self updateBackgroundColorForSelectionState:flag];
}

- (NSColor*) textColor
{
    return self.selected ? [NSColor whiteColor] : [NSColor textColor];
}

In my case I wanted an image(check mark) to indicate selection of object. Drag an ImageWell to the Collection Item nib. Set the desired image and mark it as hidden. Go to bindings inspector and bind hidden attribute to Collection View Item.

(In my case I had created a separate nib for CollectionViewItem, so its binded to File's owner. If this is not the case and Item view is in the same nib as the CollectionView then bind to Collection View Item)

Set model key path as selected and Value transformer to NSNegateBoolean. Thats it now whenever the individual cells/items are selected the image will be visible, hence indicating the selection.

Adding to Alter's answer.

To set NSBox as root item. Simply create a new IB document(say CollectionItem) and drag an NSBox to the empty area. Now add all the elements as required inside the box. Now click on File's Owner and set Custom Class as NSCollectionViewItem.

And in the nib where NSCollectionView is added change the nib name for CollectionViewItem

In the NSBox, bind the remaining elements to Files Owner. For a label it would be similar to :

Now to get the highlight color as Alter mentioned in his answer, set desired color combination in the Fill Color option, set the NSBox to transparent and bind the transparency attribute as below:

Now when Collection View Items are selected you should be able to see the fill color of the box.

In your NSCollectionViewItem subclass, override isSelected and change background color of the layer. Test in macOS 10.14 and Swift 4.2

class Cell: NSCollectionViewItem {

  override func loadView() {    
    self.view = NSView()
    self.view.wantsLayer = true
  }

  override var isSelected: Bool {
    didSet {
      self.view.layer?.backgroundColor = isSelected ? NSColor.gray.cgColor : NSColor.clear.cgColor
    }
  }
}

This was awesome, thanks alot! i was struggling with this!

To clarify for to others:

 [(PrototypeView*)[self view] setSelected:flag];
 [(PrototypeView*)[self view] setNeedsDisplay:YES];

Replace PrototypeView* with the name of your prototype class name.

In case you are digging around for the updated Swift solution, see this response.

class MyViewItem: NSCollectionViewItem {
  override var isSelected: Bool {
      didSet {
        self.view.layer?.backgroundColor = (isSelected ? NSColor.blue.cgColor : NSColor.clear.cgColor)
      }
  }
  etc...
}

Here is the complete Swift NSCollectionViewItem with selection. Don't forget to set the NSCollectioView to selectable in IB or programmatically. Tested under macOS Mojave (10.14) and High Sierra (10.13.6).

import Cocoa

class CollectionViewItem: NSCollectionViewItem {

private var selectionColor : CGColor {
    let selectionColor : NSColor = (isSelected ? .alternateSelectedControlColor : .clear)
    return selectionColor.cgColor
}

override var isSelected: Bool {
    didSet {
        super.isSelected = isSelected
        updateSelection()
        // Do other stuff if needed
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    view.wantsLayer = true
    updateSelection()
}

override func prepareForReuse() {
    super.prepareForReuse()
    updateSelection()
}

private func updateSelection() {
    view.layer?.backgroundColor = self.selectionColor
}
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!