19

我正在使用 NSSlider 控件,并且已将其配置为使用连续模式,以便在用户滑动滑块时,我可以使用滑块的当前值不断更新 NSTextField。我遇到的问题是,在用户松开旋钮之前,我不想“提交”该值,即我不希望我的应用程序考虑该值,除非用户松开滑块来表示它处于所需的值。目前,我不知道什么时候会出现这种情况。动作方法只是被连续调用,没有指示何时释放滑块。

如果可能的话,我需要一个解决方案来涵盖边缘情况,例如用户使用键盘或辅助工具(如果有的话)与滑块进行交互。我已经开始考虑使用鼠标事件,但由于我刚才概述的原因,它似乎不是一个最佳解决方案。

4

7 回答 7

30

这对我有用(并且比继承 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
    }
}
于 2012-08-22T04:37:13.787 回答
7

您也可以简单地在 action 方法中检查当前事件的类型:

- (IBAction)sliderChanged:(id)sender
{
    NSEvent *currentEvent = [[sender window] currentEvent];
    if ([currentEvent type] == NSLeftMouseUp) {
        // the slider was let go
    }
}
于 2014-04-12T17:37:30.197 回答
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
    }
}
于 2017-06-30T10:26:54.413 回答
3

我花了一点时间找到这个线程,但接受的答案(虽然旧)非常适合检测 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
    }
}

注意:正确的事件类型适用于反转鼠标按钮的人。

于 2018-06-08T20:52:19.430 回答
1

不幸的是,由于基本 Cocoa 控件的设计方式,这两种需求是矛盾的。如果您使用目标/动作机制,您仍然会以连续或非连续模式触发动作。如果您使用的是绑定,您仍然会触发 KVO。

对此的一种“快速”解决方案可能是将 NSSlider/NSSliderCell 子类化并覆盖鼠标拖动机制以调用 super 然后以 self 作为对象发布自定义“NSSliderDidChangeTemporaryValue”(或您选择的任何名称)通知。将其设置为不连续,因此更改仅在用户完成拖动时“为 realz 提交”,但您仍然可以观察“用户建议值”通知并根据需要更新 UI。

无需注意鼠标向上或以这种方式实现复杂的“不改变但仍然拖动”逻辑。

于 2012-02-23T16:45:12.897 回答
1

子类化NSSlider和实现

- (void)mouseDown:(NSEvent *)theEvent

它被调用mouseDown:,但是当知道交互结束时它被调用

- (void)mouseDown:(NSEvent *)theEvent {
    [super mouseDown:theEvent];
    NSLog(@"Knob released!");
}
于 2013-11-24T14:11:51.263 回答
1

刚刚找到了一种优雅的方法,让滑块不断更新标签,并仅在用户释放所有鼠标按钮时才存储滑块的值。

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
    }
}
于 2019-07-12T17:59:46.943 回答