7

NSSliderをサブクラス化して、ジョグダイヤルと呼ばれるコントロールを作成しようとしています。基本的に私が必要としているのは、常に中央から始まり、左または右に移動すると、コンテナに現在の値を通知する通知を頻繁に送信するスライダーです(設定できる属性によって決定されます)。つまみを離すと真ん中に戻ります。スライダーを中央に戻し、スライダーのmouseUpイベントで通知の送信を停止する機能を実装したいと思っていましたが、何らかの理由で、AppleはスライダーのmouseDownイベントの後にMouseUpイベントを無効にし、すべてのスライダー機能を処理しているようですより低いレベルで。とにかく、mouseUpイベントを取り戻すことはできますか?そうでない場合、誰かが合理的な回避策を提案できますか?

4

8 に答える 8

8

スーパークラスの実装が呼び出されていないことに気付いた場合は常に、クラスの実装mouseDragged:が追跡ループに入っていることが原因である可能性があります。これは確かにを含む多くのサブクラスに当てはまります。mouseUp:mouseDown:NSControlNSSlider

マウスアップを検出するためのより良い方法は、セルをサブクラス化し、適切な追跡方法をオーバーライドすることです。この場合、おそらく必要です- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flagが、startTracking:およびcontinueTracking:バリアントは、実行しようとしていることにも役立つ可能性があります。

于 2010-10-13T05:40:59.617 に答える
7

これは私にとってはうまくいきます(そして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:36:26.360 に答える
7

そのような状況で私が使用する(しかし発明しなかった)トリックがあります。まず、IBで、スライダーを「連続」として指定します。これにより、スライダーを動かしたときにアクションメッセージが表示されます。次に、アクションメソッドで次のようにします。

[NSObject cancelPreviousPerformRequestsWithTarget: self
  selector: @selector(finishTrack) object: nil ];
[self performSelector: @selector(finishTrack) withObject: nil
  afterDelay: 0.0];

マウスを離すと、finishTrackメソッドが呼び出されます。

于 2010-10-13T01:12:22.890 に答える
3

Kperryuaのアイデアが最もクリーンな解決策を提供するように思われるので、それを受け入れられた答えとしてマークしますが、特定の状況で機能する少しのハックを使用しなくなったので、それも共有できると思いました。

私が作成しているアプリはクロスプラットフォームである必要があるため、この目標を達成するためにCocotron(WindowsでCocoa APIの多くを実装するオープンソースプロジェクト)を使用しています。cocotronは上記のstopTracking:...メソッドをサポートしていません。

幸いなことに、小さなテストプログラムで遊んでいると、NSSliderのmouseDownメソッドをオーバーライドしてそのメソッドのスーパーを呼び出すと、マウスボタンを離すまで戻らないことがわかりました。 superを呼び出した後、mouseDownメソッド内のmouseUpにコードを含める必要があります。これは少し汚いハックですが、私たちの場合に機能する唯一の解決策のようですので、それを使用する必要があります。

于 2010-10-13T17:34:53.197 に答える
3

誰かがSwift3で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)
    }
}

...
于 2017-01-02T13:27:47.310 に答える
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
于 2013-11-24T14:16:19.270 に答える
0

同様のニーズがありましたが、NSTouchBarのスライダーが必要でした。私は、MarcPrud'hommeauxやMartinMajewskiと同様の「迅速で汚い」アプローチを使用しましたが、検査するイベントはわずかに異なります。これは、タッチバーのスライダーの連続値と最終値の両方を追跡できるようにする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)")
            }
        }
    }
}
于 2017-08-26T01:01:16.160 に答える
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)
    }
}
于 2020-12-05T15:10:47.460 に答える