NSSLIDER SUBSING: بحاجة إلى حل بديل لأحداث ماوس في مفقود (COCOA OSX)

StackOverflow https://stackoverflow.com/questions/3919905

  •  29-09-2019
  •  | 
  •  

سؤال

أحاول فئة NSSLIDER الفرعية لإنشاء عنصر تحكم يسمى DIAL JOG. في الأساس ما أحتاجه هو شريط منزلق يبدأ دائمًا في الوسط وعندما يتم نقله إلى اليسار أو اليمين ، فإنه سيرسل إشعارات في كثير من الأحيان (تحددها سمة يمكن للمرء تعيينها) لإبلاغ حاويةها بقيمتها الحالية ، ثم عندك دعك تذهب من المقبض ، وسيعود إلى الوسط. كنت آمل في تنفيذ الوظيفة لإعادة شريط التمرير إلى الوسط والتوقف عن إرسال الإخطارات في حدث Mouseup من التمرير ، لكن يبدو أنه لسبب ما تعطل Apple حدث الفأرة بعد حدث Mousedown على شريط التمرير ويتعامل مع جميع وظائف التمرير في مستوى أقل. هل هناك على أي حال يمكنني استعادة حدث 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) لتحقيق هذا الهدف ولا يدعم Cocotron التوقف: ... الطريقة المذكورة أعلاه.

لحسن الحظ ، أثناء اللعب مع برنامج اختبار صغير ، قمنا باكتشاف أنه إذا تجاوزت طريقة Mousedown في NSSlider واستدعاء هذه الطريقة الفائقة ، فإنه لا يعود حتى يتم إصدار زر الماوس ، حتى تتمكن من وضع فقط أيا كان الرمز في Mouseup داخل طريقة Mousedown بعد الاتصال إلى Super. هذا جزء من الاختراق القذر ، لكن يبدو أنه الحل الوحيد الذي سيعمل في حالتنا ، لذلك سيتعين علينا الذهاب معه.

فقط في حال كان شخص ما يتساءل عن كيفية تحقيق ذلك في 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)
    }
}

...

لا يمكن القيام بذلك إلا عن طريق التصنيف الفرعي (لن تعمل أساليب مثل mprudhom 100 ٪ لإصدار معرفة)

لبداية السحب ، تحتاج إلى التقاط becomeFirstResponder (لهذا تحتاج أيضًا إلى توفيره needsPanelToBecomeKey)

لنهاية السحب ، تحتاج إلى فئة فرعية mouseDown: (يسمى Mousedown ، ولكن يتم استدعاؤه عندما تم إصدار Knob)

ملاحظة: سيجعل هذا النهج منزلقًا مستجيبًا أولًا ، وإلغاء أي مستجيب أولي آخر

ملاحظة: نهج من 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 ، فقط أحداث مختلفة قليلاً لتفقدها. إليك الرمز السريع الذي يتيح لك تتبع كل من القيمة المستمرة والقيمة النهائية للمنزلق على شريط اللمس.

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