0

OK, here's what I have done:

  • I have an NSCollectionView
  • I wanted to be able to enable "selecting" items, and drawing a custom border when an items is selected
  • I subclassed NSCollectionViewItem (to enable selection)
  • I subclassed NSView for the NSCollectionViewItem view, in order to draw the border

The code

The view item

@implementation MSLibraryCollectionViewItem

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

The custom view

@implementation MSLibraryCollectionViewView

/***************************************
 Initialisation
 ***************************************/

- (MSLibraryCollectionViewView*)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
    }

    return self;
}

/***************************************
 Drawing
 ***************************************/

- (void)drawRect:(NSRect)rect
{
    if ([self selected]) {
        //[[NSColor redColor] setFill];
        //NSRectFill(rect);
        //[super drawRect:rect];

            NSColor* gS = [NSColor colorWithCalibratedRed:0.06 green:0.45 blue:0.86 alpha:1.0];
        NSColor* gE = [NSColor colorWithCalibratedRed:0.12 green:0.64 blue:0.94 alpha:1.0];
        NSGradient* g = [[NSGradient alloc] initWithStartingColor:gE endingColor:gS];
        NSColor *borderColor = [NSColor colorFromGradient:g];

        NSRect frameRect = [self bounds];

        if(rect.size.height < frameRect.size.height)
            return;
        NSRect newRect = NSMakeRect(rect.origin.x+5, rect.origin.y+5, rect.size.width-10, rect.size.height-10);

        NSBezierPath *textViewSurround = [NSBezierPath bezierPathWithRoundedRect:newRect xRadius:7 yRadius:7];
        [textViewSurround setLineWidth:2.0];
        [borderColor set];
        [textViewSurround stroke];
    }
}

However, the seems to be something wrong with drawing. For example:

  • When resizing the Collection View's container, a weird line appears at the outer box
  • When an Collection View item is not 100% visible (e.g. because it's been scrolled down), the selection border doesn't appear at all (while I would expect it to draw just the visible portion).

Some Examples

NSCollectionView selected item drawing issue

NSCollectionView drawing issue

What's going on?


P.S. I'm not a guru with drawing and custom views in Cocoa - so any ideas/help is more than welcome!

Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223

1 Answers1

1

You switched from asking about a collection view to talking about an outline view, but I assume that was just a mental hiccup.

  • When an Outline View item is not 100% visible (e.g. because it's been scrolled down), the selection border doesn't appear at all (while I would expect it to draw just the visible portion).

That's because of this code in your -drawRect:.

    if(rect.size.height < frameRect.size.height)
        return;

It's specifically avoiding drawing a partial selection outline.

Regarding the weird line, I doubt that has to do with your collection item view's custom drawing. Does it stop happening if you disable the custom drawing? You could experiment with using an ordinary color rather than using the third-party +colorFromGradient: code you're using.

By the way, this line:

    NSRect newRect = NSMakeRect(rect.origin.x+5, rect.origin.y+5, rect.size.width-10, rect.size.height-10);

could be written more simply as:

    NSRect newRect = NSInsetRect(rect, 5, 5);
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Well, my two main issues is the one I posted about `NSCollectionView` and another one regarding an Outline View - so that's why my mind probably mixed things up! lol. Let me give it a try and I'll get back to you! Thanks a lot! – Dr.Kameleon Oct 25 '14 at 07:01
  • I tried commenting this line and it does draw the border now, but it kinda creates a worse mess. Question: do you know of any way to "scroll" `NSCollectionView` to selected item, so that it's not clipped? – Dr.Kameleon Oct 25 '14 at 07:08
  • Well, I think I've found something here: http://stackoverflow.com/questions/18541159/nscollectionview-how-to-scroll-to-selected-item. As for the weird drawing issue for the selected item, I'm now attempting an approach with an `NSBox`, and it seems it goes much smoother... – Dr.Kameleon Oct 25 '14 at 07:27
  • I took a second look at your code. You're basing the path you draw on the `rect` parameter that's passed in. That's the dirty rect intersected with the visible rect. Since you want the selection indicator to be based on the size of the whole view, you should base it on `self.bounds`, not `rect`. – Ken Thomases Oct 25 '14 at 08:56
  • I've tried with `frameRect = [self bounds]` too. But it was a mess. Though for different reasons. I suppose I'll have to accept my fate and not mess with `drawRect:` a lot - it usually ends up bad. lol – Dr.Kameleon Oct 25 '14 at 08:59
  • Do what you like, of course, but there's no reason that doing custom drawing (in `-drawRect:`, of course) should cause problems. It's not voodoo. You don't just have to shrug and go "that's just the way it is". It's fixable. It's just a matter of being careful and getting the logic right. – Ken Thomases Oct 25 '14 at 09:30