NSView with gradient background
In Mac OS X 10.5 a new class NSGradient is introduced, that makes it really easy to work with gradients (as you might have guessed from the name). The sample code below shows a basic yet functional implementation of an NSView subclass that has either an colored or gradient background.
ColorGradientView.h
@interface ColorGradientView : NSView
{
NSColor *startingColor;
NSColor *endingColor;
int angle;
}
// Define the variables as properties
@property(nonatomic, retain) NSColor *startingColor;
@property(nonatomic, retain) NSColor *endingColor;
@property(assign) int value;
@end
ColorGradientView.m
#import "GradientView.h"
@implementation ColorGradientView
// Automatically create accessor methods
@synthesize startingColor;
@synthesize endingColor;
@synthesize angle;
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
[self setStartingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]];
[self setEndingColor:nil];
[self setAngle:270];
}
return self;
}
- (void)drawRect:(NSRect)rect {
if (endingColor == nil || [startingColor isEqual:endingColor]) {
// Fill view with a standard background color
[startingColor set];
NSRectFill(rect);
}
else {
// Fill view with a top-down gradient
// from startingColor to endingColor
NSGradient* aGradient = [[[NSGradient alloc]
initWithStartingColor:startingColor
endingColor:endingColor] autorelease];
[aGradient drawInRect:[self bounds] angle:angle];
}
}
@end
Note that the code is based on the new garbage collection mechanism available in Objective-C 2.0, so there are no retain or release calls.
In the following picture you see a gradient effect from top to bottom to give a subtle 3d appearance:
![]()
To achieve this, add a NSView in Interface Builder and define its class as ColorGradientView. In your controller, add a ColorGradientView outlet (e.g. myGradientView) and in Interface Builder attach the view to the outlet. Add the following 3 lines to the - (void)awakeFromNib method in the controller:
[myGradientView setStartingColor:
[NSColor colorWithCalibratedWhite:0.85 alpha:1.0]];
[myGradientView setEndingColor:
[NSColor colorWithCalibratedWhite:0.7 alpha:1.0]];
[myGradientView setAngle:270];
In upcoming post I will go into details of how to create the Safari like buttons that you see in the screen shot.
David:
If you are using objc-c 2.0 then why not use properties? Get rid of the setters and have in interface:
@property(copy) NSColor *startingColor;
@property(copy) NSColor *endingColor;
@property(assign) int angle;
then in implementation section:
@synthesize startingColor, endingColor, angle;
But will be good to see article on using NSGradient with buttons.
10 January 2008, 1:22 amBerrie:
Good point. Makes the code simpler and doesn’t distract from the core message.
I’ve updated the code in the posting.
10 January 2008, 6:57 amJoan Lluch:
As far as I understand the code has some memory management flaws (even if you are using garbage collection)
[A] - The following line :
startingColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];
can potentially produce a released object access since the NSColor will be autoreleased in the next run loop, so you need to include [startingColor retain] to avoid it, or simply use [self setStartingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]] ;
The code actually works because you use [myGradientView setStartingColor: ] in your controller, but the above code is still faulty.
[B] - The drawRect method produces a memory leak each time it is called (which may be potentially very often) because aGradient is never released. You must explicitly release it after using it (this is fine if garbage collection is enabled though)
Regards,
Joan
25 June 2008, 10:37 pmBerrie:
Well spotted, I’ve changed the example to fix these flaws.
26 June 2008, 6:56 am