子分类NSSLIDER:需要一个解决方法以丢失鼠标UP事件(可可OSX)
题
我正在尝试子类NSSlider创建一个称为慢跑拨盘的控件。基本上,我需要的是一个滑块,它总是从中间开始,当它移动到左或右时,它会经常发送通知(由属性确定可以设置),将其容器告知其当前值,然后让您离开旋钮,它将返回中间。我希望实现该功能,以将滑块返回中间并停止在滑块的鼠标UP事件中发送通知,但是似乎出于某种原因,Apple在滑块上的Mousedown事件后禁用了MouseUp事件,并处理所有滑块功能在较低级别。无论如何,我可以将MouseUp事件恢复吗?如果没有,任何人都可以建议合理的解决方法吗?
解决方案
每当您注意到超类的实施 mouseDragged:
或者 mouseUp:
没有被打电话,很可能是因为班级的实施 mouseDown:
进入跟踪循环。许多人当然是如此 NSControl
子类包括 NSSlider
.
检测鼠标向上的更好方法是子类别分类并覆盖适当的跟踪方法。在这种情况下,您可能想要 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag
, ,但是 startTracking:
和 continueTracking:
变体可能对您要尝试的工作也很有用。
其他提示
在这种情况下,我有一个技巧(但没有发明)。首先,在IB中,将滑块指定为“连续”,以便在移动滑块时收到动作消息。然后,在操作方法中,执行此操作:
[NSObject cancelPreviousPerformRequestsWithTarget: self
selector: @selector(finishTrack) object: nil ];
[self performSelector: @selector(finishTrack) withObject: nil
afterDelay: 0.0];
鼠标释放后, finishTrack
方法将被调用。
这对我有用(比起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
}
}
看来克佩里亚(Kperryua)的想法将提供最清洁的解决方案,因此我将其标记为公认的答案,但是我最终使用了一些针对我的特定情况有用的黑客,所以我也认为我也可以分享这一点。
我要制作的应用需要是跨平台,因此我使用的是Cocotron(一个开源项目,在Windows上实现了可可API)来实现此目标,而Cocotron不支持停工:...上述方法。
幸运的是,在玩一个小测试程序时,我们发现,如果您覆盖NSSlider的Mousedown方法并调用该方法的超级呼叫超级后,Mousedown方法内的MouseUp中应有任何代码。这有点肮脏的黑客,但这似乎是在我们的情况下可以使用的唯一解决方案,因此我们必须继续使用。
以防万一有人想知道如何在Swift 3中使用NSSlider Dant State的状态连续实现:
@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)
}
}
...
这只能通过子分类来完成(例如,@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
我也有类似的需求,但是对于nstouchbar上的滑块。我使用了类似的“快速和肮脏”方法,例如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)")
}
}
}
}