我正在使用NSSlider控件,并且我已将其配置为使用连续模式,以便在用户滑动时可以使用滑块的当前值不断更新NSTextField。我遇到的问题是,在用户放开旋钮之前我不想“提交”该值,即我不希望我的应用程序考虑该值,除非用户放开滑块来表示它达到了理想的价值。目前,我无法知道何时是这样的;动作方法只是连续调用,没有指示滑块何时被释放。
如果可能的话,我需要一个解决方案来覆盖边缘情况,例如用户使用键盘或辅助工具(如果有这样的东西)与滑块交互。我开始研究使用鼠标事件,但由于我刚刚概述的原因,它似乎不是最佳解决方案。
答案 0 :(得分:28)
这对我有用(并且比子类化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
}
}
答案 1 :(得分:7)
您还可以在操作方法中简单地检查当前事件的类型:
- (IBAction)sliderChanged:(id)sender
{
NSEvent *currentEvent = [[sender window] currentEvent];
if ([currentEvent type] == NSLeftMouseUp) {
// the slider was let go
}
}
答案 2 :(得分:2)
使用其他答案建议的当前应用程序或窗口事件可能在某种程度上更简单,但不是防弹 - 可以通过编程方式停止跟踪+检查其他问题的相关注释。对滑块和滑块单元进行子类化更加可靠和直接,但是,更新接口构建器中的类是一个缺点:
// 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
}
}
答案 3 :(得分:2)
我花了一点时间才找到这个帖子,但是接受的答案(虽然陈旧)非常适合检测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
}
}
注意:右事件类型可以说明已经改变了鼠标按钮的人。
答案 4 :(得分:1)
不幸的是,由于基本Cocoa控件的设计方式,这两个需求是矛盾的。如果您正在使用目标/动作机制,则仍然以连续或非连续模式触发动作。如果您正在使用Bindings,那么您仍然会触发KVO。
对此的一个“快速”解决方案可能是子类NSSlider / NSSliderCell并覆盖鼠标拖动机制以调用super然后发布自定义“NSSliderDidChangeTemporaryValue”(或您选择的任何名称)通知,以self作为对象。保持设置为不连续,因此当用户完成拖动时,更改仅“为realz”提交,但您仍然可以观察“用户建议的值”通知,并根据需要更新UI。
无需注意鼠标或以这种方式实现复杂的“不改变但仍然没有动态”的逻辑。
答案 5 :(得分:1)
子类NSSlider
并实施
- (void)mouseDown:(NSEvent *)theEvent
它被称为mouseDown:
,但在已知交互结束时被调用
- (void)mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
NSLog(@"Knob released!");
}
答案 6 :(得分:0)
只是找到了一种优雅的方式来使滑块连续更新标签,并且仅当用户释放所有鼠标按钮时才存储滑块的值。
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
}
}