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:
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:
NSBox
es 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With