cocoaswiftnsslider

Change NSSlider bar color


I've got an NSWindow and an horizontal NSSlider.

I'd like to change the color of the right part of the slider bar when the window background color changes.

enter image description here

enter image description here

Currently, the right part of the bar isn't visible anymore when the window background is dark.

Note: the window background is actually a gradient I'm drawing by overriding drawRect in a subclass of the window's view.

I thought I could change the slider bar fill color by subclassing NSSliderCell and overriding some method like drawBarInside but I don't understand how it works: should I make a little square image and draw it repeatedly in the rect after the knob? Or maybe just draw a colored line? I've never done this before.

I've looked at this question and it's interesting but it's about drawing the knob and I don't need that for now.

I also had a look at this one which seemed very promising, but when I try to mimic this code my slider bar just disappears...

In this question they use drawWithFrame and it looks interesting but again I'm not sure how it works.

I would like to do this with my own code instead of using a library. Could somebody give me a hint about how to do this please? :)

I'm doing this in Swift but I can read/use Objective-C if necessary.


Solution

  • This is correct, you have to subclass the NSSliderCell class to redraw the bar or the knob.

    NSRect is just a rectangular container, you have to draw inside this container. I made an example based on an custom NSLevelIndicator that I have in one of my program.

    First you need to calculate the position of the knob. You must pay attention to the control minimum and maximum value. Next you draw a NSBezierPath for the background and another for the left part.

    Custom NSSlider control bar

    #import "MyCustomSlider.h"
    
    @implementation MyCustomSlider
    
    - (void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped {
    
    //  [super drawBarInside:rect flipped:flipped];
    
        rect.size.height = 5.0;
    
        // Bar radius
        CGFloat barRadius = 2.5;
    
        // Knob position depending on control min/max value and current control value.
        CGFloat value = ([self doubleValue]  - [self minValue]) / ([self maxValue] - [self minValue]);
    
        // Final Left Part Width
        CGFloat finalWidth = value * ([[self controlView] frame].size.width - 8);
    
        // Left Part Rect
        NSRect leftRect = rect;
        leftRect.size.width = finalWidth;
    
        NSLog(@"- Current Rect:%@ \n- Value:%f \n- Final Width:%f", NSStringFromRect(rect), value, finalWidth);
    
        // Draw background track
        NSBezierPath* bg = [NSBezierPath bezierPathWithRoundedRect: rect xRadius: barRadius yRadius: barRadius];
        [NSColor.orangeColor setFill];
        [bg fill];
    
        // Draw active track
        NSBezierPath* active = [NSBezierPath bezierPathWithRoundedRect: leftRect xRadius: barRadius yRadius: barRadius];
        [NSColor.purpleColor setFill];
        [active fill];
    
    
    }
    
    @end