更改 NSSlider 栏颜色

Posted

技术标签:

【中文标题】更改 NSSlider 栏颜色【英文标题】:Change NSSlider bar color 【发布时间】:2015-04-19 12:18:05 【问题描述】:

我有一个 NSWindow 和一个水平 NSSlider。

当窗口背景颜色改变时,我想改变滑动条右侧部分的颜色。

目前,当窗口背景较暗时,栏的右侧部分不再可见。

注意:窗口背景实际上是我通过在窗口视图的子类中覆盖 drawRect 来绘制的渐变。

我想我可以通过子类化NSSliderCell 并覆盖drawBarInside 之类的方法来更改滑块填充颜色,但我不明白它是如何工作的:我应该制作一个小方形图像并在矩形中重复绘制吗旋钮后?或者也许只是画一条彩色线?我以前从来没有这样做过。

我看过this question,这很有趣,但它是关于绘制旋钮的,我现在不需要。

我还查看了this one,这似乎很有希望,但是当我尝试模仿这段代码时,我的滑块消失了......

在this question 中,他们使用drawWithFrame,它看起来很有趣,但我又不确定它是如何工作的。

我想用我自己的代码而不是使用库来做到这一点。有人可以给我一个关于如何做到这一点的提示吗? :)

我在 Swift 中执行此操作,但如有必要,我可以阅读/使用 Objective-C。

【问题讨论】:

我猜您无法从标准界面访问这些属性。第二个链接看起来很有希望。我想你应该让它运行起来。基本上,滑块是一个简单的视图,其中旋钮跟随拖放操作。 【参考方案1】:

这是正确的,您必须继承 NSSliderCell 类来重绘条形旋钮

NSRect 只是一个矩形容器,你必须在这个容器内绘制。我根据我的一个程序中的自定义NSLevelIndicator 做了一个示例。

首先您需要计算旋钮的位置。您必须注意控制最小值和最大值。 接下来为背景绘制一个NSBezierPath,为左侧绘制另一个。

#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 Left Part
    NSBezierPath* bg = [NSBezierPath bezierPathWithRoundedRect: rect xRadius: barRadius yRadius: barRadius];
    [NSColor.orangeColor setFill];
    [bg fill];

    // Draw Right Part
    NSBezierPath* active = [NSBezierPath bezierPathWithRoundedRect: leftRect xRadius: barRadius yRadius: barRadius];
    [NSColor.purpleColor setFill];
    [active fill];




@end

【讨论】:

非常感谢,它看起来很有帮助。我会尽快适应 Swift 并对其进行测试。 像魅力一样工作!我将您的答案标记为已接受,并使用我根据您的示例制作的 Swift 版本更新我的答案。谢谢! 当旋钮移动时,有时会在旋钮左侧出现类似this 的模糊现象。你认为我需要强制 UI 在某处刷新吗? 如果你说旋钮左边的效果,你可以调整最后一个值(8),通常是旋钮宽度:CGFloat finalWidth = value * ([[self controlView] frame].size.width - 8); 谢谢,但是这个拉圈的时候好像有重影的颜色。【参考方案2】:

首先,我创建了一个滑块的图像并将其复制到我的项目中。

然后我在drawBarInside 方法中使用此图像在条形图rect 正常图像之前绘制,所以我们只会看到剩余部分(我想保留蓝色部分完好无损)。

这必须在NSSliderCell 的子类中完成:

class CustomSliderCell: NSSliderCell 

    let bar: NSImage

    required init(coder aDecoder: NSCoder) 
        self.bar = NSImage(named: "bar")!
        super.init(coder: aDecoder)
    

    override func drawBarInside(aRect: NSRect, flipped: Bool) 
        var rect = aRect
        rect.size = NSSize(width: rect.width, height: 3)
        self.bar.drawInRect(rect)
        super.drawBarInside(rect, flipped: flipped)
    


专业人士:它有效。 :)

缺点:它移除了条形的圆边,我还没有找到重新绘制它的方法。

更新:

我制作了已接受答案的 Swift 版本,效果很好:

class CustomSliderCell: NSSliderCell 

    required init(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
    

    override func drawBarInside(aRect: NSRect, flipped: Bool) 
        var rect = aRect
        rect.size.height = CGFloat(5)
        let barRadius = CGFloat(2.5)
        let value = CGFloat((self.doubleValue - self.minValue) / (self.maxValue - self.minValue))
        let finalWidth = CGFloat(value * (self.controlView!.frame.size.width - 8))
        var leftRect = rect
        leftRect.size.width = finalWidth
        let bg = NSBezierPath(roundedRect: rect, xRadius: barRadius, yRadius: barRadius)
        NSColor.orangeColor().setFill()
        bg.fill()
        let active = NSBezierPath(roundedRect: leftRect, xRadius: barRadius, yRadius: barRadius)
        NSColor.purpleColor().setFill()
        active.fill()
    


【讨论】:

【参考方案3】:

斯威夫特 5

class CustomSliderCell: NSSliderCell 
    required init(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
    

    override func drawBar(inside aRect: NSRect, flipped: Bool) 
        var rect = aRect
        rect.size.height = CGFloat(5)
        let barRadius = CGFloat(2.5)
        let value = CGFloat((self.doubleValue - self.minValue) / (self.maxValue - self.minValue))
        let finalWidth = CGFloat(value * (self.controlView!.frame.size.width - 8))
        var leftRect = rect
        leftRect.size.width = finalWidth
        let bg = NSBezierPath(roundedRect: rect, xRadius: barRadius, yRadius: barRadius)
        NSColor.orange.setFill()
        bg.fill()
        let active = NSBezierPath(roundedRect: leftRect, xRadius: barRadius, yRadius: barRadius)
        NSColor.purple.setFill()
        active.fill()
    

【讨论】:

【参考方案4】:

我完全没有重绘或覆盖单元格就实现了这一点。使用“假色”滤镜效果很好,而且只有几个代码!

class RLSlider: NSSlider 
init() 
    super.init(frame: NSZeroRect)
    addFilter()


required init?(coder: NSCoder) 
    super.init(coder: coder)
    addFilter()


func addFilter() 
    let colorFilter = CIFilter(name: "CIFalseColor")!
    colorFilter.setDefaults()
    colorFilter.setValue(CIColor(cgColor: NSColor.white.cgColor), forKey: "inputColor0")
    colorFilter.setValue(CIColor(cgColor: NSColor.yellow.cgColor), forKey: "inputColor1")

//        colorFilter.setValue(CIColor(cgColor: NSColor.yellow.cgColor), forKey: "inputColor0")
//        colorFilter.setValue(CIColor(cgColor: NSColor.yellow.cgColor), forKey: "inputColor1")

    self.contentFilters = [colorFilter]


【讨论】:

这不会让你得到你想要的确切颜色,它也会替换旋钮颜色。

以上是关于更改 NSSlider 栏颜色的主要内容,如果未能解决你的问题,请参考以下文章

自定义标签栏背景颜色。如何更改标签栏背景的颜色?

如何在情节提要上更改标签栏背景颜色和标签栏项目颜色

在 Swift 中更改导航栏颜色

更改标签栏的背景颜色

Android:API 级别低于 21 的状态栏颜色更改

更改状态栏背景颜色