Adding a control/button bar to the bottom of an NSOutlineView configured as a source list

1.3k views Asked by At

I am trying to add a button bar to the bottom of my NSOutlineView-based source list, as seen in many Mac applications (both Apple and third-party), as seen in these screenshots:

Xcode Source List Footer Mail Source List Footer

To describe it textually, the control bar shares the source list's specially styled gradient background (or under Yosemite, "vibrancy") without overlapping any of the source list's content. In attempt to reproduce this effect, I have tried the following methods so far:

  1. Adding extra "padding" with the height of the button bar to the bottom of the source list's scroll view's clip view and allowing the bar to overlap the source list view. This only works if the button bar background is opaque and doesn't move the scrollbar out of the way.
  2. Using NSBoxes with their backgrounds set to the NSColor provided by the source list's backgroundColor property. This requires a lot of forced redraws to draw correctly (notably on window active/inactive states) but otherwise looks perfect. Similar behavior is seen with a custom NSView set up to draw the gradient background.

Is there any other method that can be used to achieve this? #2 is the closest I've been able to come but given the problems it brings, it's obviously not meant to be used by third-party developers.

Doing this with Vibrancy in Yosemite should be simple, being a matter of checking for Yosemite and inserting an NSVisualEffectView with vibrancy turned on. Getting it right under 10.8/10.9, on the other hand…

I could sidestep this problem entirely by using the built-in bottom bar drawing provided by NSWindow, but the color-merged approach is visually much cleaner, much more strongly associates controls with their parent panes, and seems to be the style of choice more and more often these days. If at all possible I'd like to use it in my own application.

3

There are 3 answers

0
Andrew On BEST ANSWER

I was able to get this working by subclassing NSView and using KVO to observe color changes when the containing window's key state changes. It does not work on 10.10 for the reasons you mentioned (vibrancy), but it does work perfectly on 10.9.

The Sourcelist background colors thread on the Apple Mailing List was what solved it for me.

Interface:

#import <Cocoa/Cocoa.h>

@interface SourceListColoredView : NSView
@end

Implementation:

#import "SourceListColoredView.h"

@interface SourceListColoredView ()
@property (nonatomic, strong) NSColor *backgroundColor;
@property (nonatomic, assign, getter = isObservingKeyState) BOOL observingKeyState;
@end

@implementation SourceListColoredView

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self addWindowKeyStateObservers];
    }
    return self;
}

- (void)awakeFromNib
{
    NSOutlineView *outlineView = [[NSOutlineView alloc] init];
    [outlineView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
    self.backgroundColor = [outlineView backgroundColor];
    [self addWindowKeyStateObservers];
}

- (void)addWindowKeyStateObservers
{
    if (!self.isObservingKeyState) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(redisplay)
                                                     name:NSWindowDidBecomeKeyNotification
                                                   object:[self window]];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(redisplay)
                                                     name:NSWindowDidResignKeyNotification
                                                   object:[self window]];
    }
    self.observingKeyState = YES;
}

- (void)redisplay
{
    [self setNeedsDisplay:YES];
}

- (void)dealloc
{
    if (self.isObservingKeyState) {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:[self window]];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:[self window]];
    }
}

- (void)drawRect:(NSRect)dirtyRect
{
    [_backgroundColor setFill];
    NSRectFill(dirtyRect);
}

@end

You may have to move the code in -awakeFromNib somewhere else depending on how you initialize the view.

0
yageek On

You can simply add an NSButton with a Gradient Effect.

Take a look at the source code of https://github.com/Perspx/PXSourceList.

Open the XIB for View Based Table you will have an example on how to achieve it.

2
zarghol On

I found the solution : I put a NSBox like you, at the bottom of the View at the left of the SplitView. Here is the hierarchy of my views :

Hierarchical view of components in Interface Builder

the solution is to fill correct parameters in the inspector of the NSBox :

parameters of the box

I have here set the box type to custom and the fill color to white.

I hope this answer helped you.