我正在尝试子类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)")
            }
        }
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top