我正在尝试子类化NSSlider来创建一个名为慢速拨号的控件。基本上我需要的是一个总是从中间开始的滑块,当它向左或向右移动时,它会经常发送通知(由可以设置的属性确定)通知其容器当前值,然后当你让你去旋钮,它会回到中间。我希望实现将滑块返回到中间并停止在滑块的mouseUp事件中发送通知的功能但似乎由于某种原因苹果在滑块上的mouseDown事件之后禁用MouseUp事件并处理所有滑块功能在较低的水平。无论如何,我可以恢复mouseUp事件吗?如果没有,任何人都可以提出合理的解决方法
答案 0 :(得分:6)
对于这种情况,我使用了一种技巧(但没有发明)。首先,在IB中,将滑块指定为“连续”,以便在移动滑块时获取操作消息。然后,在action方法中,执行以下操作:
[NSObject cancelPreviousPerformRequestsWithTarget: self
selector: @selector(finishTrack) object: nil ];
[self performSelector: @selector(finishTrack) withObject: nil
afterDelay: 0.0];
释放鼠标后,将调用finishTrack
方法。
答案 1 :(得分:6)
每当您注意到超级类的mouseDragged:
或mouseUp:
的实现未被调用时,很可能是因为该类的mouseDown:
实现进入了跟踪循环。对于包括NSControl
的许多NSSlider
子类来说,情况确实如此。
检测鼠标的更好方法是对单元格进行子类化并覆盖相应的跟踪方法。在这种情况下,您可能需要- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag
,但startTracking:
和continueTracking:
变体可能对您正在尝试的内容有用。
答案 2 :(得分:6)
这对我有用(并且比子类化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
}
}
答案 3 :(得分:3)
似乎Kperryua的想法会提供最干净的解决方案,所以我会将其标记为已接受的答案,但我最终使用了一些适合我特定情况的黑客攻击,所以我想我也可以分享它。
我正在制作的应用程序需要跨平台,因此我使用Cocotron(一个在Windows上实现大部分Cocoa API的开源项目)来实现这一目标,而cocotron不支持stopTracking:...方法上文提到的。
幸运的是,在玩一个小测试程序时,我们发现如果你覆盖NSSlider的mouseDown方法并调用该方法的super,它会在鼠标按钮释放后才会返回,所以你可以在调用super之后将mouseUp中的任何代码放在mouseDown方法中。这是一个肮脏的黑客,但它似乎是唯一的解决方案将在我们的情况下工作,所以我们将不得不采取它。
答案 4 :(得分:2)
以防万一有人想知道如何在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)
}
}
...
答案 5 :(得分:0)
这只能通过子类化来完成(像@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
答案 6 :(得分:0)
我有类似的需求,但对于NSTouchBar上的滑块。我使用过类似的快速和肮脏的#34;接近Marc Prud' hommeaux和Martin Majewski,只是略微不同的事件要检查。这是Swift代码,可让您跟踪触摸栏上滑块的连续值和最终值。
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)")
}
}
}
}
答案 7 :(得分:0)
我认为将NSSlider子类化比在目标中添加额外的代码更好。
此方法转发完整的mouseup和mousedown事件(对于获取键修饰符可能很有用),并准备在需要时处理其他类型的事件。
此滑块在鼠标向下移动时存储初始值,因此我们可以在鼠标向上移动时获取初始值和最终值。这对于不可撤销的操作非常有用。
class Slider: NSSlider {
var mouseDownClosure: ((Slider, NSEvent)->Void)?
var mouseUpClosure: ((Slider, NSEvent)->Void)?
var initialValue: Double = 0
override func sendAction(_ action: Selector?, to target: Any?) -> Bool {
let result = super.sendAction(action, to: target)
if let event = NSApplication.shared.currentEvent {
switch event.type {
case .leftMouseUp:
mouseUpClosure?(self, event)
case .leftMouseDown:
mouseDownClosure?(self, event)
default:
break
}
}
return result
}
override func mouseDown(with event: NSEvent) {
initialValue = self.doubleValue
super.mouseDown(with: event)
}
}