2010-10-12 3 views
6

Ich versuche, NSSlider Unterklasse zu erstellen, um ein Steuerelement namens Jog Dial zu erstellen. Was ich eigentlich brauche, ist ein Schieberegler, der immer in der Mitte beginnt und wenn er nach links oder rechts bewegt wird, wird er immer wieder Benachrichtigungen senden (bestimmt durch ein einstellbares Attribut), das seinen Container über seinen aktuellen Wert informiert lass dich von dem Knopf los, es wird in die Mitte zurückkehren. Ich hatte gehofft, die Funktionalität zu implementieren, um den Schieberegler in die Mitte zu bringen und Benachrichtigungen im MouseUp-Ereignis des Schiebereglers zu stoppen, aber aus irgendeinem Grund deaktiviert Apple das MouseUp-Ereignis nach einem mouseDown-Ereignis auf dem Schieberegler und behandelt alle Schiebereglerfunktionen auf einer niedrigeren Ebene. Kann ich das mouseUp-Event trotzdem zurückbekommen? Wenn nicht, kann jemand eine angemessene Abhilfe vorschlagen?Subclassing NSSlider: Benötigen Sie eine Problemumgehung für fehlende Mouse-up-Ereignisse (Cocoa OSX)

Antwort

5

Jedes Mal, wenn Sie feststellen, dass eine Implementierung von mouseDragged: oder mouseUp: der übergeordneten Klasse nicht genannt zu werden, dann ist es sehr wahrscheinlich, weil die Implementierung der Klasse von mouseDown: eine Tracking-Schleife eintritt. Dies trifft sicherlich auf viele Unterklassen zu, einschließlich NSControl, einschließlich NSSlider.

Eine bessere Möglichkeit, eine Maus zu erkennen, besteht darin, die Zelle zu unterklassifizieren und die entsprechende Verfolgungsmethode zu überschreiben. In diesem Fall möchten Sie wahrscheinlich - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag, aber die startTracking: und continueTracking: Varianten könnten sich auch als nützlich erweisen für das, was Sie versuchen zu tun.

6

Es gibt einen Trick, den ich für solche Situationen verwende (aber nicht erfunden habe). Definieren Sie zuerst in IB den Schieberegler als "kontinuierlich", sodass Sie beim Verschieben des Schiebereglers Aktionsnachrichten erhalten. Dann wird in der Aktion-Methode, dies tun:

[NSObject cancelPreviousPerformRequestsWithTarget: self 
    selector: @selector(finishTrack) object: nil ]; 
[self performSelector: @selector(finishTrack) withObject: nil 
    afterDelay: 0.0]; 

Nachdem die Maustaste losgelassen wird, wird die finishTrack Methode aufgerufen werden.

3

Es scheint, dass Kperryuas Idee die sauberste Lösung bieten würde, also werde ich das als die akzeptierte Antwort markieren, aber ich endete mit einem kleinen Hack, der für meine spezifische Situation funktionierte, also dachte ich, ich könnte das auch teilen.

Die App, die ich mache, muss plattformübergreifend sein, daher verwende ich Cocotron (ein Open-Source-Projekt, das einen Großteil der Cocoa-API unter Windows implementiert), um dieses Ziel zu erreichen, und cocotron unterstützt das stopTracking nicht: ... Methode oben erwähnt.

Glücklicherweise haben wir beim Spielen mit einem kleinen Testprogramm festgestellt, dass, wenn Sie die mouseDown-Methode des NSSlider überschreiben und das Super dieser Methode aufrufen, es erst zurückkehrt, wenn die Maustaste losgelassen wird Sie können einfach den Code in mouseUp innerhalb der mouseDown-Methode nach dem Aufruf von super einfügen. Dies ist ein bisschen ein schmutziger Hack, aber es scheint die einzige Lösung zu sein, die in unserem Fall funktionieren wird, so dass wir damit fortfahren müssen.

6

Dies funktioniert für mich (und ist einfacher als Subklassen NSSlider):

- (IBAction)sizeSliderValueChanged:(id)sender { 
    NSEvent *event = [[NSApplication sharedApplication] currentEvent]; 
    BOOL startingDrag = event.type == NSLeftMouseDown; 
    BOOL endingDrag = event.type == NSLeftMouseUp; 
    BOOL dragging = event.type == NSLeftMouseDragged; 

    NSAssert(startingDrag || endingDrag || dragging, @"unexpected event type caused slider change: %@", event); 

    if (startingDrag) { 
     NSLog(@"slider value started changing"); 
     // do whatever needs to be done when the slider starts changing 
    } 

    // do whatever needs to be done for "uncommitted" changes 
    NSLog(@"slider value: %f", [sender doubleValue]); 

    if (endingDrag) { 
     NSLog(@"slider value stopped changing"); 
     // do whatever needs to be done when the slider stops changing 
    } 
} 
+1

Dies funktioniert nicht immer (beginnen Sie zu ziehen und dann gehen Sie zum Menü, Sie werden nie den endingDrag registrieren) –

0

Dies ist nur durch Subclassing getan werden kann (Methoden wie von @mprudhom für Know-Release nicht arbeiten 100%)

für den Anfang des Schlepp, müssen Sie becomeFirstResponder (hierfür auch needsPanelToBecomeKey müssen gesichert werden) fangen

für Ende ziehen, müssen Sie mouseDown: (ihre Namen mouseDown- Unterklasse, aber es wird calle d, wenn der Knopf freigegeben)

Hinweis: Dieser Ansatz wird Ihren Regler, um den Ersthelfer, Cancelling jeden beliebigen anderen Ersthelfer

Hinweis machen: Ansatz von mprudhom

@implementation CustomSlider 

- (void)mouseDown:(NSEvent *)theEvent { 
    [super mouseDown:theEvent]; 
    NSLog(@"OK"); 
} 

- (BOOL)needsPanelToBecomeKey { 
    [super needsPanelToBecomeKey]; 
    return YES; 
} 

- (BOOL)becomeFirstResponder { 
    [super becomeFirstResponder]; 
    NSLog(@"Became first responder."); 
    return YES; 
} 

@end 
1

Nur falls jemand fragen, wie dies in Swift 3 mit einem NSSlider zu erreichen, das ist Zustand kontinuierlich eingestellt:

@IBOutlet weak var mySlider: NSSlider! 

... 

@IBAction func handlingNSSliderChanges(_ sender: Any) { 

    // Perform updates on certain events 
    if sender is NSSlider{ 
     let event = NSApplication.shared().currentEvent 

     if event?.type == NSEventType.leftMouseUp { 
      print("LeftMouseUp event inside continuous state NSSlider") 
     } 
    } 

    // Do continuous updates 
    if let value = mySlider?.integerValue{ 
     demoLabel.stringValue = String(value) 
    } 
} 

... 
0

ich ein ähnliches Bedürfnis gehabt, aber für einen Schieber auf einem NSTouchBar . Ich benutzte einen ähnlichen "schnellen und schmutzigen" Ansatz wie Marc Prud'hommeaux und Martin Majewski, nur etwas andere Ereignisse zu inspizieren. Hier ist der Swift-Code, mit dem Sie sowohl den kontinuierlichen Wert UND den Endwert des Schiebereglers auf einer Touchbar verfolgen können.

func sliderValueChanged(_ sender: NSSlider) { 
    let doubleValue = sender.doubleValue 

    Swift.print("Continuous value: \(doubleValue)") 

    // find out if touch event has ended 
    let event = NSApplication.shared().currentEvent 
    if event?.type == NSEventType.directTouch { 
     if let endedTouches = event?.touches(matching: .ended, in: nil) { 
      if (endedTouches.count > 0) { 
       Swift.print("The final value was: \(doubleValue)") 
      } 
     } 
    } 
} 
Verwandte Themen