Определите, когда ручка NSSlider «отпущена» в непрерывном режиме - PullRequest
15 голосов
/ 23 февраля 2012

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

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

Ответы [ 7 ]

28 голосов
/ 22 августа 2012

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

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

- (IBAction)sliderChanged:(id)sender
{
    NSEvent *currentEvent = [[sender window] currentEvent];
    if ([currentEvent type] == NSLeftMouseUp) {
        // the slider was let go
    }
}
2 голосов
/ 08 июня 2018

Мне понадобилось немного времени, чтобы найти эту ветку, но принятый ответ (хотя и старый) отлично подходит для обнаружения изменений состояния 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
    }
}

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

2 голосов
/ 30 июня 2017

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

// 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
    }
}
1 голос
/ 24 ноября 2013

Подкласс NSSlider и реализация

- (void)mouseDown:(NSEvent *)theEvent

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

- (void)mouseDown:(NSEvent *)theEvent {
    [super mouseDown:theEvent];
    NSLog(@"Knob released!");
}
1 голос
/ 23 февраля 2012

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

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

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

0 голосов
/ 12 июля 2019

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

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