更改 NSSlider 栏颜色
Change NSSlider bar color
我有一个 NSWindow 和一个水平 NSSlider。
我想在 window 背景颜色改变时改变滑块右侧部分的颜色。
目前,当 window 背景较暗时,栏的右侧部分不再可见。
注意:window 背景实际上是我通过覆盖 window 视图的子类中的 drawRect
绘制的渐变。
我想我可以通过子类化 NSSliderCell
并重写 drawBarInside
之类的方法来更改滑块填充颜色,但我不明白它是如何工作的:我应该制作一个小方形图像和在旋钮后的矩形中重复绘制它?或者也许只是画一条彩色线?我以前从未这样做过。
我看过 this question,它很有趣,但它是关于绘制旋钮的,我现在不需要它。
我还查看了 this one,这看起来很有前途,但是当我尝试模仿这段代码时,我的滑块就消失了...
在 this question 他们使用 drawWithFrame
看起来很有趣,但我还是不确定它是如何工作的。
我想用我自己的代码而不是使用库来做这件事。有人可以给我一个关于如何做到这一点的提示吗? :)
我在 Swift 中执行此操作,但如果需要,我可以 read/use Objective-C。
首先,我创建了一个滑块图像并将其复制到我的项目中。
然后我在 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()
}
}
这是正确的,您必须将 class NSSliderCell
class 子 重新绘制条形图 或 旋钮.
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
我完全没有重绘或覆盖单元格就实现了这一点。使用 "False color" 过滤器似乎工作得很好而且只有几个代码!
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]
}
}
Swift 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()
}
}
我有一个 NSWindow 和一个水平 NSSlider。
我想在 window 背景颜色改变时改变滑块右侧部分的颜色。
目前,当 window 背景较暗时,栏的右侧部分不再可见。
注意:window 背景实际上是我通过覆盖 window 视图的子类中的 drawRect
绘制的渐变。
我想我可以通过子类化 NSSliderCell
并重写 drawBarInside
之类的方法来更改滑块填充颜色,但我不明白它是如何工作的:我应该制作一个小方形图像和在旋钮后的矩形中重复绘制它?或者也许只是画一条彩色线?我以前从未这样做过。
我看过 this question,它很有趣,但它是关于绘制旋钮的,我现在不需要它。
我还查看了 this one,这看起来很有前途,但是当我尝试模仿这段代码时,我的滑块就消失了...
在 this question 他们使用 drawWithFrame
看起来很有趣,但我还是不确定它是如何工作的。
我想用我自己的代码而不是使用库来做这件事。有人可以给我一个关于如何做到这一点的提示吗? :)
我在 Swift 中执行此操作,但如果需要,我可以 read/use Objective-C。
首先,我创建了一个滑块图像并将其复制到我的项目中。
然后我在 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()
}
}
这是正确的,您必须将 class NSSliderCell
class 子 重新绘制条形图 或 旋钮.
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
我完全没有重绘或覆盖单元格就实现了这一点。使用 "False color" 过滤器似乎工作得很好而且只有几个代码!
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]
}
}
Swift 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()
}
}