Subklassen NSSlider: Sie benötigen eine Lösung für fehlende Maus Events (Cocoa OSX)

StackOverflow https://stackoverflow.com/questions/3919905

  •  29-09-2019
  •  | 
  •  

Frage

Ich versuche NSSlider Unterklasse ein Steuerelement erstellen Jog-Dial genannt. Im Grunde, was ich brauche, ist ein Schieber, der immer in der Mitte beginnt und wenn es nach links bewegt wird oder rechts wird es Meldungen jeder so oft (durch ein Attribut kann man bestimmt gesetzt) ??schicken Behälter von seinem aktuellen Wert zu informieren, dann, wenn Sie lassen Sie sich von dem Knopf gehen, wird es in die Mitte zurück. Ich habe gehofft, um die Funktionalität zu implementieren, um die Schieber in der Mitte und das Ende des Senden von Benachrichtigungen in dem mouseUp-Ereignisse des Schiebers zurückzukehren, aber es scheint, dass das Ereignis MouseUp nach einem mouseDown- Ereignisse aus irgendeinem Grunde Apfel deaktiviert auf dem Schieber und Griffen all Slider-Funktionalität auf einem niedrigeren Niveau. Gibt es trotzdem kann ich das mouseUp Ereignis zurück? Wenn nicht, kann jemand eine angemessene Abhilfe vorschlagen?

War es hilfreich?

Lösung

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 gilt sicherlich für viele NSControl Unterklassen einschließlich NSSlider.

Eine bessere Möglichkeit, eine Maus zu erkennen, bis die Zelle Unterklasse und die entsprechende Tracking-Methode außer Kraft setzen. 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 nützlich erweisen, als auch für das, was Sie zu tun versuchen.

Andere Tipps

Es gibt einen Trick, dass ich (aber nicht erfunden) für solche Situationen. Zuerst in IB bezeichnet den Schieber als „kontinuierlich“, so dass Sie Aktionsmeldungen erhalten werden, wenn der Schieber bewegt wird. Dann wird in der Aktion-Methode, dies zu tun:

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

Nach dem Loslassen der Maustaste, wird die finishTrack Methode aufgerufen werden.

Das 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
    }
}

Es scheint, dass Kperryua Idee die sauberste Lösung zur Verfügung stellen würde, damit ich, dass als akzeptierte Antwort markiert wird, aber ich endete ein bisschen ein Hack, der für meine spezifische Situation gearbeitet, damit ich dachte, dass ich das auch teilen könnten.

Die App, dass ich Bedürfnisse mache Cross-Plattform sein, so dass ich bin mit Cocotron (ein Open-Source-Projekt, dass Geräte viel von dem Cocoa-API unter Windows) dieses Ziel und cocotron zu erreichen ist die Stoptracking nicht unterstützt: ... Methode oben erwähnt.

Zum Glück, während sie mit einem kleinen Testprogramm Herumspielen haben wir es wurde entdeckt, dass, wenn Sie die MouseDown- Methode des NSSlider außer Kraft setzen und die Super dieser Methode aufrufen, es nicht zurück, bis die Maustaste losgelassen wird, so dass Sie kann nur, was Code setzt in mouseUp innerhalb der mouseDown- Methode nach dem Aufruf von super sein sollte. Dies ist ein bisschen wie ein schmutziger Hack, aber es scheint die einzige Lösung zu sein, die in unserem Fall funktionieren werden, so dass wir mit ihm gehen gehen zu müssen.

Für den Fall, jemand fragt sich, wie dies in Swift erreichen 3 mit einem NSSlider das ist Zustand eingestellt ist kontinuierlich:

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

...

Dies ist nur durch Subklassen (wie Methoden aus @mprudhom wird zu 100% für die Know-Release nicht) getan werden kann,

Für den Anfang des Schlepp, müssen Sie fangen becomeFirstResponder (dazu müssen Sie auch needsPanelToBecomeKey zur Verfügung zu stellen)

für das Ende ziehen, müssen Sie Unterklasse mouseDown: (sein genannt mouseDown-, aber es wird aufgerufen, wenn der Knopf losgelassen)

Hinweis: dieser Ansatz wird Ihren Regler, um den Ersthelfer machen, anderen Strom Ersthelfer Cancelling

Hinweis: 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

Ich hatte ein ähnliches Bedürfnis, aber für einen Schieber auf einem NSTouchBar. Ich habe ein ähnliches „quick-and dirty“ Ansatz als Marc Prud'hommeaux und Martin Majewski, nur etwas andere Ereignisse zu untersuchen. Hier ist der Swift Code, den Sie sowohl den kontinuierlichen Wert und den Endwert des Schiebers auf einer Sensorleiste verfolgen kann.

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)")
            }
        }
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top