Search code examples
macoscocoanssegmentedcontrol

Tinting a NSSegmentedControl... how?


I have a NSSegmentedControl that must be selected before the user can pass to the next phase in a wizard. I can disable the next button forcing the user to guess that something is missing but I would like to flash the control red, something like a tint or border, to call attention, if the user press NEXT without selecting an option on the control.

I did not find a single page on google telling how to do that. Is that possible to do that in cocoa? how can I do that for one NSSegmentedControl?


Solution

  • I use a tinted segmented control in Tembo. The purpose here is not to draw attention to the control, but to have it blend-in with a colored navigation bar. Top right of the screenshot.

    Rather than draw a segmented control from scratch, I have the standard implementation draw into an NSImage which I then tint before drawing to to the view.

    The same principle could be used to draw attention to the control. You will need to call setNeedsDisplay whenever you change the tint color

    @interface TintedSegmentedCell : NSSegmentedCell
    {
        NSMutableDictionary *_frames;
    }
    
    @end
    
    
    @implementation TintedSegmentedControl
    
    @synthesize tintColor = _tintColor;
    
    - (void)dealloc
    {
        [_tintColor release], _tintColor = nil;
    
        [super dealloc];
    }
    
    + (Class)cellClass
    {
        return [TintedSegmentedCell class];
    }
    
    @end
    
    
    @implementation TintedSegmentedCell
    
    - (void)drawSegment:(NSInteger)segment inFrame:(NSRect)frame withView:(NSView *)controlView
    {
        [_frames setObject:[NSValue valueWithRect:frame] forKey:[NSNumber numberWithInteger:segment]];
    
        [super drawSegment:segment inFrame:frame withView:controlView];
    }
    
    - (void)drawWithFrame:(NSRect)frame inView:(NSView *)view
    {
        if ([view isKindOfClass:[TintedSegmentedControl class]]) {
            NSColor *tintColor = [(TintedSegmentedControl*)view tintColor];
    
            if (tintColor != nil) {
                NSRect bounds = frame;
    
                bounds.origin.x = 0;
                bounds.origin.y = 0;
    
                NSSize size = bounds.size;
                NSImage *image = [[[NSImage alloc] initWithSize:size] autorelease];
    
                NSInteger segmentCount = [self segmentCount];
                NSMutableDictionary *frames = [NSMutableDictionary dictionaryWithCapacity:segmentCount];
    
                _frames = frames;
    
                [image lockFocus];
                {
                    [super drawWithFrame:bounds inView:view];
                }
                [image unlockFocus];
    
                NSImage *tintedImage = [[image hh_imageTintedWithColor:[NSColor blackColor]] hh_imageTintedWithColor:tintColor];
    
                [tintedImage drawInRect:frame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
    
                NSImage *overlayImage = [[[NSImage alloc] initWithSize:size] autorelease];
    
                [overlayImage lockFocus];
                {
                    _frames = nil;
    
                    for (NSInteger segment = 0; segment < segmentCount; segment++) {
                        NSRect frameRect = [[frames objectForKey:[NSNumber numberWithInteger:segment]] rectValue];
    
                        [self drawSegment:segment inFrame:frameRect withView:view];
                    }
                }
                [overlayImage unlockFocus];
    
                [overlayImage drawInRect:frame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
    
                return;
            }
        }
    
        [super drawWithFrame:frame inView:view];
    }
    
    @end