2015-04-19 20 views
7

Mam NSWindow i poziomy NSSlider.Zmiana koloru paska NSSlider

Chciałbym zmienić kolor prawej części suwaka, gdy zmienia się kolor tła okna.

enter image description here

enter image description here

Obecnie, w prawej części paska nie jest już widoczny, gdy okno tło jest ciemne.

Uwaga: tło okna jest w rzeczywistości gradientem, który rysuję przez przesłonięcie drawRect w podklasie widoku okna.

Myślałem, że mogę zmienić kolor Suwak wypełnienia przez instacji NSSliderCell i przesłanianie jakąś metodę jak drawBarInside ale ja nie rozumiem, jak to działa: powinienem zrobić trochę kwadratowy obraz i wyciągnąć go wielokrotnie w rect po pokrętło? A może po prostu narysuj kolorową linię? Nigdy tego nie robiłem.

Spojrzałem na this question i jest to interesujące, ale chodzi o rysowanie pokrętła i nie potrzebuję tego na razie.

miałem również do obejrzenia this one co wydawało się bardzo obiecujące, ale gdy próbuję naśladować ten kod mój Suwak prostu znika ...

W this question oni używają drawWithFrame i wygląda interesujące, ale znowu jestem nie wiem, jak to działa.

Chciałbym zrobić to z własnym kodem, zamiast korzystać z biblioteki. Czy ktoś może dać mi wskazówkę, jak to zrobić? :)

Robię to w Swift, ale w razie potrzeby mogę odczytać/użyć Objective-C.

+0

Chyba nie ma dostępu do tych atrybutów ze standardowego interfejsu. Drugi link wygląda obiecująco. Chyba powinieneś go uruchomić. Zasadniczo suwak jest prostym widokiem, w którym pokrętło podąża za przeciąganiem i upuszczaniem. –

Odpowiedz

10

To prawda, trzeba podklasy klasy NSSliderCell do przerysuj pasek lub pokrętło.

NSRect to tylko prostokątny pojemnik, trzeba w nim wsunąć. Zrobiłem przykład oparty na niestandardowym NSLevelIndicator, który mam w jednym z moich programów.

Najpierw należy obliczyć położenie pokrętła. Musisz zwrócić uwagę na minimalną i maksymalną wartość kontrolną. Następnie narysuj NSBezierPath dla tła i drugą dla lewej części.

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 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 
+0

Wielkie dzięki, wygląda bardzo pomocne. Dostosuję to do Swifta i przetestuję go tak szybko, jak tylko będę mógł. – Moritz

+0

Działa jak urok! Zaznaczam twoją odpowiedź jako zaakceptowaną i aktualizuję ją swoją wersją Swift, którą zrobiłem z twojego przykładu. Dziękuję Ci ! – Moritz

+0

Jest * czasami * trochę rozmycia, jak [to] (https://www.evernote.com/shard/s89/sh/3815fed6-c28a-43ba-a8a5-ebe11c96f90f/47918faa41523aa7cfa4c5a0e962ae35/deep/0/BoomBox.png) na po lewej stronie pokrętła, gdy się porusza. Czy myślisz, że muszę zmusić interfejs do odświeżenia? – Moritz

9

Najpierw utworzyłem obraz suwaka i skopiowałem go do mojego projektu.

Następnie użyłem tego obrazu w metodzie drawBarInside narysować na pasku rectprzed normalny jeden, więc zobaczymy tylko część pozostałej części (chciałem zachować niebieską część nienaruszona).

To musi być zrobione w podklasie 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) 
    } 

} 

Pro: to działa. :)

Con: usuwa zaokrąglone krawędzie taktu i nie znalazłem jeszcze sposobu na przerysowanie tego.

custom nsslider

UPDATE:

Zrobiłem Swift wersji zaakceptowanej odpowiedzi, to działa bardzo dobrze:

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() 
    } 

} 
+1

Działa świetnie. Czas zaktualizować górną odpowiedź dla Swift 3, 4. W następnym roku możesz zrobić to ponownie za pomocą Swift 5, a następnie Swift 6, a następnie Apple wyjdzie z nowym językiem i wycofa całą wykonaną pracę. – wcochran