2012-02-08 29 views
7

मैं NSButton बनाना चाहता हूं जो क्लिक होने पर एक क्रिया भेजता है, लेकिन जब इसे 1 या दो सेकंड के लिए दबाया जाता है तो यह एक एनएसएमएनयू दिखाता है। बिल्कुल इस प्रश्न के रूप में here, लेकिन चूंकि वह उत्तर मेरी समस्या का समाधान नहीं करता है, इसलिए मैंने फिर से पूछने का फैसला किया।एनएसबीटन - देरी से एनएसएमएनयू - उद्देश्य-सी/कोको

उदाहरण के तौर पर, खोजक पर जाएं, एक नई विंडो खोलें, कुछ फ़ोल्डरों के माध्यम से नेविगेट करें और फिर बैक बटन पर क्लिक करें: आप पिछले फ़ोल्डर पर जाते हैं। अब बैक बटन पर क्लिक करके रखें: मेनू प्रदर्शित होता है। मुझे नहीं पता कि NSPopUpButton के साथ ऐसा कैसे करें।

उत्तर

8

NSSegmentedControl का उपयोग करें।

नियंत्रण में setMenu:forSegment: भेजकर एक मेनू जोड़ें (आईबी में menu आउटलेट से कुछ भी कनेक्ट करना चाल नहीं करेगा)। नियंत्रण से जुड़े एक क्रिया है (यह महत्वपूर्ण है)।

जैसा आपने वर्णन किया है ठीक उसी तरह काम करना चाहिए।

+1

बहुत बुरा आप NSSegmentedControl के लिए ऊंचाई ऊंचाई सेट नहीं कर सकते - मुझे उस बटन को एक बड़े बटन से जुड़ा होना चाहिए। – zrxq

+0

पूरी तरह से काम किया! धन्यवाद! – Alex

+0

आईबी में चारों ओर खेलने के साथ अच्छी चाल, आप इस तरह एक वास्तव में सुरुचिपूर्ण नियंत्रण प्राप्त कर सकते हैं। –

5

NSPopUpButton का उप-वर्ग बनाएं और mouseDown/mouseUp ईवेंट ओवरराइड करें।

mouseDownsuper के कार्यान्वयन को कॉल करने से पहले एक पल के लिए ईवेंट देरी हो और केवल तभी माउस को दबाया जा रहा हो।

mouseUp घटना बटन के target/action फायरिंग से पहले selectedMenuItemnil को (और इसलिए selectedMenuItemIndex हो जाएगा -1) निर्धारित किया है।

एकमात्र अन्य मुद्दा तेजी से क्लिक को संभालना है, जहां एक क्लिक के लिए टाइमर उस समय आग लग सकता है जब माउस कुछ भविष्य के क्लिक के लिए नीचे आ जाता है। NSTimer का उपयोग करने और इसे अमान्य करने के बजाय, मैंने mouseDown ईवेंट के लिए एक सरल काउंटर चुना है और काउंटर बदल गया है तो जमानत हो गई है।

// MyClickAndHoldPopUpButton.h 
@interface MyClickAndHoldPopUpButton : NSPopUpButton 

@end 

// MyClickAndHoldPopUpButton.m 
@interface MyClickAndHoldPopUpButton() 

@property BOOL mouseIsDown; 
@property BOOL menuWasShownForLastMouseDown; 
@property int mouseDownUniquenessCounter; 

@end 

@implementation MyClickAndHoldPopUpButton 

// highlight the button immediately but wait a moment before calling the super method (which will show our popup menu) if the mouse comes up 
// in that moment, don't tell the super method about the mousedown at all. 
- (void)mouseDown:(NSEvent *)theEvent 
{ 
    self.mouseIsDown = YES; 
    self.menuWasShownForLastMouseDown = NO; 
    self.mouseDownUniquenessCounter++; 
    int mouseDownUniquenessCounterCopy = self.mouseDownUniquenessCounter; 

    [self highlight:YES]; 

    float delayInSeconds = [NSEvent doubleClickInterval]; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
    if (self.mouseIsDown && mouseDownUniquenessCounterCopy == self.mouseDownUniquenessCounter) { 
     self.menuWasShownForLastMouseDown = YES; 
     [super mouseDown:theEvent]; 
    } 
    }); 
} 

// if the mouse was down for a short enough period to avoid showing a popup menu, fire our target/action with no selected menu item, then 
// remove the button highlight. 
- (void)mouseUp:(NSEvent *)theEvent 
{ 
    self.mouseIsDown = NO; 

    if (!self.menuWasShownForLastMouseDown) { 
    [self selectItem:nil]; 

    [self sendAction:self.action to:self.target]; 
    } 

    [self highlight:NO]; 
} 

@end 
+1

सुंदर! यही वह है जिसकी तलाश में मैं हूं। बहुत बुरा यह इस तरह की चीज के लिए ऐप किट में मानक नियंत्रण नहीं है (जो अजीब है क्योंकि ऐप्पल अपने यूआई कन्वेंशन का उपयोग अपने स्वयं के ऐप्स के * बहुत * में करता है)। – aapierce

+1

'देरीइन सेकेंड' के लिए 'NSEvent का उपयोग करने पर विचार करें।निरंतर '0.2' के बजाय डबलक्लिक इंटरवल'। यह उपयोगकर्ता की माउस हैंडलिंग वरीयताओं के अनुसार देरी को समायोजित करेगा। लंबे समय तक डबल-क्लिक समय वाले उपयोगकर्ताओं के लिए तेज़, कम देरी, धीमी, अधिक देरी वाले उपयोगकर्ताओं के लिए, अधिक डबल-क्लिक समय वाले उपयोगकर्ताओं के लिए। –

+1

धन्यवाद @ ग्राहम मिल्न, मैंने ऐसा करने के लिए अपना जवाब अपडेट कर दिया है। –

1

किसी को अभी भी इस की जरूरत है, यहाँ मेरी समाधान एक सादे NSButton, नहीं एक खंडित नियंत्रण के आधार पर दिया गया है:

यहाँ कोड मैं अपने उपवर्ग में उपयोग कर रहा हूँ है।

सबक्लास एनएसबटन और एक कस्टम mouseDown लागू करें जो वर्तमान रन लूप के भीतर टाइमर शुरू करता है। mouseUp में, जांचें कि टाइमर को निकाल दिया गया है या नहीं। उस स्थिति में, इसे रद्द करें और डिफ़ॉल्ट कार्रवाई करें।

यह एक बहुत ही सरल दृष्टिकोण है, यह किसी भी NSButton के साथ काम करता है जिसका आप आईबी में उपयोग कर सकते हैं।

नीचे

कोड:

- (void)mouseDown:(NSEvent *)theEvent { 
    [self setHighlighted:YES]; 
    [self setNeedsDisplay:YES]; 

    _menuShown = NO; 
    _timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(showContextMenu:) userInfo:nil repeats:NO]; 

    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode]; 
} 

- (void)mouseUp:(NSEvent *)theEvent { 
    [self setHighlighted:NO]; 
    [self setNeedsDisplay:YES]; 

    [_timer invalidate]; 
    _timer = nil; 

    if(!_menuShown) { 
     [NSApp sendAction:[self action] to:[self target] from:self]; 
    } 

    _menuShown = NO; 
} 

- (void)showContextMenu:(NSTimer*)timer { 
    if(!_timer) { 
     return; 
    } 

    _timer = nil; 
    _menuShown = YES; 

    NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@"Contextual Menu"]; 

    [[theMenu addItemWithTitle:@"Beep" action:@selector(beep:) keyEquivalent:@""] setTarget:self]; 
    [[theMenu addItemWithTitle:@"Honk" action:@selector(honk:) keyEquivalent:@""] setTarget:self]; 

    [theMenu popUpMenuPositioningItem:nil atLocation:NSMakePoint(self.bounds.size.width-8, self.bounds.size.height-1) inView:self]; 

    NSWindow* window = [self window]; 

    NSEvent* fakeMouseUp = [NSEvent mouseEventWithType:NSLeftMouseUp 
               location:self.bounds.origin 
             modifierFlags:0 
              timestamp:[NSDate timeIntervalSinceReferenceDate] 
              windowNumber:[window windowNumber] 
               context:[NSGraphicsContext currentContext] 
              eventNumber:0 
              clickCount:1 
               pressure:0.0]; 

    [window postEvent:fakeMouseUp atStart:YES]; 

    [self setState:NSOnState]; 
} 

मैं अपने GitHub पर एक working sample पोस्ट किया है।