Подкласс NSSlider: нужен обходной путь для пропущенных событий Mouse Up (Cocoa OSX)

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

  •  29-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь подключить NSSlider, чтобы создать элемент управления, называемый циферблатом JOG. По сути, мне нужен ползунок, который всегда начинается в середине, и когда он перемещается влево или вправо, он будет отправлять уведомления так часто (определяется атрибутом, который можно установить), информируя свой контейнер своего текущего значения, затем, когда вы Отпустите ручку, она вернется в середину. Я надеялся реализовать функциональность, чтобы вернуть слайдер в середину и прекратить отправлять уведомления в событии MouseUp о слайдере, но кажется, что по какой -то причине Apple отключает событие MouseUp после события Mousedown на ползунке и обрабатывает все функции слайдера на более низком уровне. В любом случае, я могу вернуть событие MouseUp? Если нет, может ли кто -нибудь предложить разумный обходной путь?

Это было полезно?

Решение

Всякий раз, когда вы замечаете, что внедрение суперкласса mouseDragged: или mouseUp: не вызывается, это наиболее вероятно, потому что внедрение класса mouseDown: Входит в петлю отслеживания. Это, безусловно, верно для многих NSControl Подклассы, включая NSSlider.

Лучшим способом обнаружения мыши является подкласс ячейки и переопределить соответствующий метод отслеживания. В этом случае вы, вероятно, хотите - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag, но startTracking: и continueTracking: Варианты могут оказаться полезными для того, что вы пытаетесь сделать.

Другие советы

Есть трюк, который я использую (но не изобрел) для таких ситуаций. Во -первых, в IB назначьте ползунок «непрерывным», чтобы вы получили сообщения действий, когда слайдер перемещается. Затем, в методе действия, сделайте это:

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

После того, как мышь выйдет finishTrack Метод будет вызван.

Это работает для меня (и проще, чем подклассная 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
    }
}

Похоже, что идея Kperryua предоставила бы самое чистое решение, поэтому я отмечу это как принятый ответ, но я закончил, используя немного взлома, который работал для моей конкретной ситуации, поэтому я подумал, что могу поделиться этим.

Приложение, которое я делаю, должно быть кросс -платформой, поэтому я использую Cocotron (проект с открытым исходным кодом, который реализует большую часть API какао в Windows) для достижения этой цели, и Cocotron не поддерживает остановку: ... ... упомянутый выше метод.

К счастью, играя с небольшой тестовой программой, мы сделали, что было обнаружено, что если вы переопределяете метод Mousedown NSSlider и позвоните в супер этого метода, она не вернется, пока кнопка мыши не будет выпущена, так что вы можете просто положить Какой бы код должен находиться в мышке внутри метода Mousedown после вызова Super. Это немного грязный взлом, но, похоже, это единственное решение, которое будет работать в нашем случае, поэтому нам придется пойти с этим.

На всякий случай, если кто -то задается вопросом, как достичь этого в Swift 3 с NSSLider, который утверждает, что непрерывно:

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

...

Это может быть сделано только путем подкласса (такие методы, как @mprudhom, не будут работать на 100% для выпуска знания)

Для начала сопротивления вам нужно поймать becomeFirstResponder (Для этого вам также необходимо предоставить needsPanelToBecomeKey)

Для конца перетаскивания вам нужно подкласс mouseDown: (Это называется Mousedown, но его называют, когда выпустил ручку)

Примечание: этот подход сделает ваш ползунок первым респондентом, отменив любого другого текущего первого респондента

Примечание: подход от 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

У меня была похожая потребность, но для слайдера на NSTOUCHBAR. Я использовал аналогичный «быстрый и грязный» подход, как Марк Пруд'хоммо и Мартин Мажеюски, немного разные события для проверки. Вот быстрый код, который позволяет вам отслеживать как непрерывное значение, так и конечное значение слайдера на сенсорной панели.

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)")
            }
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top