Подтвердить что ты не робот

Определите, когда ручка NSSlider "отпускает" в непрерывном режиме

Я использую элемент управления NSSlider, и я настроил его на использование непрерывного режима, чтобы я мог постоянно обновлять NSTextField с текущим значением ползунка, пока пользователь его передвигает. Проблема в том, что я не хочу "фиксировать" значение до тех пор, пока пользователь не отпустит ручку, т.е. Я не хочу, чтобы мое приложение учитывало значение, если только пользователь не отпустил ползунок для обозначения он по желаемому значению. На данный момент я не знаю, когда это произойдет; метод действия просто вызывается постоянно, без указания того, когда слайдер был освобожден.

Если это возможно, мне нужно решение, которое будет охватывать такие крайние случаи, как пользователь, взаимодействующий с ползунком с клавиатурой или инструментами доступности (если есть такая вещь). Я начал изучать использование событий мыши, но это не казалось оптимальным решением по причинам, которые я только что изложил.

4b9b3361

Ответ 1

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

Ответ 2

Вы также можете просто проверить тип текущего события в методе действий:

- (IBAction)sliderChanged:(id)sender
{
    NSEvent *currentEvent = [[sender window] currentEvent];
    if ([currentEvent type] == NSLeftMouseUp) {
        // the slider was let go
    }
}

Ответ 3

Использование текущего приложения или события окна, как было предложено другими ответами, может быть проще в некоторой степени, но не пуленепробиваемый - отслеживание может быть остановлено программно + проверить соответствующие комментарии для других проблем. Подклассификация как слайдера, так и слайдерной ячейки намного более надежна и проста, однако обновление классов в построителе интерфейсов является недостатком:

// This is Swift 3.

import AppKit

class Slider: NSSlider
{
    fileprivate(set) var tracking: Bool = false
}

class SliderCell: NSSliderCell
{
    override func startTracking(at startPoint: NSPoint, in controlView: NSView) -> Bool {
        (self.controlView as? Slider)?.tracking = true
        return super.startTracking(at: startPoint, in: controlView)
    }

    override func stopTracking(last lastPoint: NSPoint, current stopPoint: NSPoint, in controlView: NSView, mouseIsUp flag: Bool) {
        super.stopTracking(last: lastPoint, current: stopPoint, in: controlView, mouseIsUp: flag)
        (self.controlView as? Slider)?.tracking = false
    }
}

Ответ 4

Мне понадобилось немного времени, чтобы найти эту ветку, но принятый ответ (хотя и старый) отлично подходит для обнаружения изменений состояния NSSlider (значение слайдера перестало меняться, так как я искал главное)!

Ответ в Swift (Swift 4.1):

let slider = NSSlider(value: 1,
                      minValue: 0,
                      maxValue: 4,
                      target: self,
                      action: #selector(sliderValueChanged(sender:)))

. . .

@objc func sliderValueChanged(sender: Any) {

    guard let slider = sender as? NSSlider, 
          let event = NSApplication.shared.currentEvent else { return }

    switch event.type {
    case .leftMouseDown, .rightMouseDown:
        print("slider value started changing")
    case .leftMouseUp, .rightMouseUp:
        print("slider value stopped changing: \(slider.doubleValue)")
    case .leftMouseDragged, .rightMouseDragged:
        print("slider value changed: \(slider.doubleValue)")
    default:
        break
    }
}

Примечание: правильные типы событий учитывают тех, кто перевернул свои кнопки мыши 🤔.

Ответ 5

К сожалению, две потребности противоречивы из-за того, как разработаны основные элементы управления Cocoa. Если вы используете механизм "цель/действие", вы все равно запускаете действие в непрерывном или не непрерывном режиме. Если вы используете Bindings, вы все равно запускаете KVO.

Одним из "быстрых" решений для этого может быть подкласс NSSlider/NSSliderCell и переопределить механизмы перетаскивания мышью для вызова супер, а затем опубликовать пользовательское "NSSliderDidChangeTemporaryValue" (или любое другое имя, которое вы выбираете) с помощью self как объекта. Оставьте его set NOT, чтобы оно не было непрерывным, поэтому изменение выполняется только для realz, когда пользователь выполнил перетаскивание, но вы все равно можете заметить уведомление "пользовательское предложение" и обновить свой пользовательский интерфейс, как вы пожелаете.

Не нужно следить за мышью или реализовать сложную логику "не меняйте-все-таки-im-still-draggin".

Ответ 6

Подкласс NSSlider и реализовать

- (void)mouseDown:(NSEvent *)theEvent

он называется mouseDown:, но его вызывается, когда знаковое взаимодействие заканчивается

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

Ответ 7

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

class YourViewController: NSViewController {
    @IBOutlet weak var slider: NSSlider!
    @IBOutlet weak var label: NSTextField!

    @objc var sliderValue = 0

    override func awakeFromNib() {
        sliderValue = 123 // init the value to whatever you like

        slider.bind(NSBindingName("value"), to: self, withKeyPath: "sliderValue")
        label.bind(NSBindingName("value"),  to: self, withKeyPath: "sliderValue")
    }

    @IBAction func sliderMoved(_ sender: NSSlider) {
        // return if a mouse button is pressed
        guard NSEvent.pressedMouseButtons == 0 else { return }

        // do something with the value
    }
}