2011-03-24 7 views
8

के लिए फ्रेम कैसे प्राप्त करें, क्या इसे कोको में स्टेटस बार में जोड़ने के बाद NSStatusItem का फ्रेम प्राप्त करना संभव है? जब मेरा ऐप लॉन्च होता है, तो मैं सिस्टम स्टेटस बार में एक आइटम जोड़ रहा हूं, और जानना चाहता हूं कि यह कहां स्थित था, यह संभव है।NSStatusItem

उत्तर

1

आप स्थिति मद पर एक कस्टम दृश्य सेट किया है:

NSRect statusRect = [[statusItem view] frame]; 
NSLog(@"%@", [NSString stringWithFormat:@"%.1fx%.1f",statusRect.size.width, statusRect.size.height]); 

अन्यथा मैं इसे उपलब्ध और दस्तावेज APIs का उपयोग संभव है नहीं लगता।

संपादित करें: शामिल टिप्पणियां।

+2

मेरे लिए काम नहीं किया। एक NSStatusItem में एक डिफ़ॉल्ट दृश्य प्रतीत नहीं होता है, इसलिए [statusItem व्यू] शून्य लौटाता है। – blutfink

+0

जैसा कि दस्तावेज़ कहता है, "स्टेटस बार में रिसीवर की स्थिति पर प्रदर्शित कस्टम दृश्य देता है।", NSStatusItem का दृश्य नहीं। –

+1

यह केवल तभी काम करता है जब आपने स्थिति आइटम पर कस्टम दृश्य सेट किया हो। – Fabian

-3

आप इस तरह खिड़की इवर हैक कर सकते हैं:

@interface NSStatusItem (Hack) 

- (NSRect)hackFrame; 

@end 

@implementation NSStatusItem (Hack) 

- (NSRect)hackFrame 
{ 
    int objSize = class_getInstanceSize([NSObject class]) ; 
    id * _ffWindow = (void *)self + objSize + sizeof(NSStatusBar*) + sizeof(CGFloat) ; 
    NSWindow * window = *_ffWindow ; 

    return [window frame] ; 
} 

@end 

यह एक कस्टम दृश्य के बिना स्थिति मदों के लिए उपयोगी है।

शेर

25

पर परीक्षण किया गया निम्नलिखित काम करने के लिए लगता है - मैं आईओएस अनुप्रयोगों के लिए इसी तरह के समाधान देखा है और माना जाता है कि वे क्योंकि आप अभी भी मानक एसडीके तरीकों का उपयोग कर रहे हैं app की दुकान के लिए प्रस्तुत करने की अनुमति है।

NSRect frame = [[statusBarItem valueForKey:@"window"] frame]; 
+2

यह काम करता है, और जब आप स्थिति आइटम के लिए कस्टम दृश्य सेट नहीं करते हैं तो उपयोगी होता है। – Fabian

+1

यह मानता है कि NSStatusItem (यह मानते हुए कि 'statusBarItem' क्या है) में कुछ ऐसा है जो केवीसी' विंडो 'संपत्ति के रूप में उपयोग कर सकता है। यह मामला होने की गारंटी नहीं है। यदि आप कभी भी दूर जाते हैं तो आपको एक अपवाद मिलेगा (इस कुंजी के लिए केवीसी-अनुरूप नहीं)। मैं इस गुजरने वाली ऐप स्टोर समीक्षा पर भी भरोसा नहीं करता हूं, या तो यदि वे पहले से नहीं हैं, तो वे किसी दिन यह जांचना शुरू कर सकते हैं कि आप केवीसी का उपयोग कैसे करते हैं (इस तरह के उपयोग की तलाश में, जहां आप निजी तरीके/ivars तक पहुंच रहे हैं)। –

+1

क्या यह एक निजी एपीआई कॉल है जो आपको ऐप स्टोर समीक्षा प्रक्रिया से संभावित रूप से अस्वीकार कर सकता है? –

24

10,10 के साथ, NSStatusItem एक button संपत्ति है कि एक कस्टम दृश्य की स्थापना के बिना स्थिति आइटम स्थिति प्राप्त करने के लिए इस्तेमाल किया जा है।

NSStatusBarButton *statusBarButton = [myStatusItem button]; 
NSRect rectInWindow = [statusBarButton convertRect:[statusBarButton bounds] toView:nil]; 
NSRect screenRect = [[statusBarButton window] convertRectToScreen:rectInWindow]; 
NSLog(@"%@", NSStringFromRect(screenRect)); 
+0

बहुत बढ़िया टिप, लेकिन 10.10 अभी तक काफी लोकप्रिय नहीं है। :( –

0

किसी भी निजी एपीआई के बिना ऐसा करना संभव है। एनएसएसस्क्रीन के लिए यहां एक श्रेणी है। यह मेनू बार पर स्थिति आइटम की छवि का पता लगाने के लिए छवि विश्लेषण का उपयोग करता है। सौभाग्य से, कंप्यूटर वास्तव में तेज़ हैं। :)

जब तक आप जानते हैं कि स्थिति आइटम की छवि कैसी दिखती है, और इसे एनएसआईमेज के रूप में पास कर सकती है, तो इस विधि को इसे ढूंढना चाहिए।

अंधेरे मोड के साथ-साथ नियमित मोड के लिए भी काम करता है। ध्यान दें कि आपके द्वारा पारित छवि काली होनी चाहिए। रंगीन छवियां शायद इतनी अच्छी तरह से काम नहीं करेंगे।

@implementation NSScreen (LTStatusItemLocator) 

// Find the location of IMG on the screen's status bar. 
// If the image is not found, returns NSZeroPoint 
- (NSPoint)originOfStatusItemWithImage:(NSImage *)IMG 
{ 
    CGColorSpaceRef  csK = CGColorSpaceCreateDeviceGray(); 
    NSPoint    ret = NSZeroPoint; 
    CGDirectDisplayID screenID = 0; 
    CGImageRef   displayImg = NULL; 
    CGImageRef   compareImg = NULL; 
    CGRect    screenRect = CGRectZero; 
    CGRect    barRect = CGRectZero; 
    uint8_t    *bm_bar = NULL; 
    uint8_t    *bm_bar_ptr; 
    uint8_t    *bm_compare = NULL; 
    uint8_t    *bm_compare_ptr; 
    size_t    bm_compare_w, bm_compare_h; 
    BOOL    inverted = NO; 
    int     numberOfScanLines = 0; 
    CGFloat    *meanValues = NULL; 

    int     presumptiveMatchIdx = -1; 
    CGFloat    presumptiveMatchMeanVal = 999; 


    // If the computer is set to Dark Mode, set the "inverted" flag 
    NSDictionary *globalPrefs = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain]; 
    id style = globalPrefs[@"AppleInterfaceStyle"]; 
    if ([style isKindOfClass:[NSString class]]) { 
     inverted = (NSOrderedSame == [style caseInsensitiveCompare:@"dark"]); 
    } 

    screenID = (CGDirectDisplayID)[self.deviceDescription[@"NSScreenNumber"] integerValue]; 

    screenRect = CGDisplayBounds(screenID); 

    // Get the menubar rect 
    barRect = CGRectMake(0, 0, screenRect.size.width, 22); 

    displayImg = CGDisplayCreateImageForRect(screenID, barRect); 
    if (!displayImg) { 
     NSLog(@"Unable to create image from display"); 
     CGColorSpaceRelease(csK); 
     return ret; // I would normally use goto(bail) here, but this is public code so let's not ruffle any feathers 
    } 

    size_t bar_w = CGImageGetWidth(displayImg); 
    size_t bar_h = CGImageGetHeight(displayImg); 

    // Determine scale factor based on the CGImageRef we got back from the display 
    CGFloat scaleFactor = (CGFloat)bar_h/(CGFloat)22; 

    // Greyscale bitmap for menu bar 
    bm_bar = malloc(1 * bar_w * bar_h); 
    { 
     CGContextRef bmCxt = NULL; 

     bmCxt = CGBitmapContextCreate(bm_bar, bar_w, bar_h, 8, 1 * bar_w, csK, kCGBitmapAlphaInfoMask&kCGImageAlphaNone); 

     // Draw the menu bar in grey 
     CGContextDrawImage(bmCxt, CGRectMake(0, 0, bar_w, bar_h), displayImg); 

     uint8_t minVal = 0xff; 
     uint8_t maxVal = 0x00; 
     // Walk the bitmap 
     uint64_t running = 0; 
     for (int yi = bar_h/2; yi == bar_h/2; yi++) 
     { 
      bm_bar_ptr = bm_bar + (bar_w * yi); 
      for (int xi = 0; xi < bar_w; xi++) 
      { 
       uint8_t v = *bm_bar_ptr++; 
       if (v < minVal) minVal = v; 
       if (v > maxVal) maxVal = v; 
       running += v; 
      } 
     } 
     running /= bar_w; 
     uint8_t threshold = minVal + ((maxVal - minVal)/2); 
     //threshold = running; 


     // Walk the bitmap 
     bm_bar_ptr = bm_bar; 
     for (int yi = 0; yi < bar_h; yi++) 
     { 
      for (int xi = 0; xi < bar_w; xi++) 
      { 
       // Threshold all the pixels. Values > 50% go white, values <= 50% go black 
       // (opposite if Dark Mode) 

       // Could unroll this loop as an optimization, but probably not worthwhile 
       *bm_bar_ptr = (*bm_bar_ptr > threshold) ? (inverted?0x00:0xff) : (inverted?0xff:0x00); 
       bm_bar_ptr++; 
      } 
     } 


     CGImageRelease(displayImg); 
     displayImg = CGBitmapContextCreateImage(bmCxt); 

     CGContextRelease(bmCxt); 
    } 


    { 
     CGContextRef bmCxt = NULL; 
     CGImageRef img_cg = NULL; 

     bm_compare_w = scaleFactor * IMG.size.width; 
     bm_compare_h = scaleFactor * 22; 

     // Create out comparison bitmap - the image that was passed in 
     bmCxt = CGBitmapContextCreate(NULL, bm_compare_w, bm_compare_h, 8, 1 * bm_compare_w, csK, kCGBitmapAlphaInfoMask&kCGImageAlphaNone); 

     CGContextSetBlendMode(bmCxt, kCGBlendModeNormal); 

     NSRect imgRect_og = NSMakeRect(0,0,IMG.size.width,IMG.size.height); 
     NSRect imgRect = imgRect_og; 
     img_cg = [IMG CGImageForProposedRect:&imgRect context:nil hints:nil]; 

     CGContextClearRect(bmCxt, imgRect); 
     CGContextSetFillColorWithColor(bmCxt, [NSColor whiteColor].CGColor); 
     CGContextFillRect(bmCxt, CGRectMake(0,0,9999,9999)); 

     CGContextScaleCTM(bmCxt, scaleFactor, scaleFactor); 
     CGContextTranslateCTM(bmCxt, 0, (22. - IMG.size.height)/2.); 

     // Draw the image in grey 
     CGContextSetFillColorWithColor(bmCxt, [NSColor blackColor].CGColor); 
     CGContextDrawImage(bmCxt, imgRect, img_cg); 

     compareImg = CGBitmapContextCreateImage(bmCxt); 


     CGContextRelease(bmCxt); 
    } 




    { 
     // We start at the right of the menu bar, and scan left until we find a good match 
     int numberOfScanLines = barRect.size.width - IMG.size.width; 

     bm_compare = malloc(1 * bm_compare_w * bm_compare_h); 
     // We use the meanValues buffer to keep track of how well the image matched for each point in the scan 
     meanValues = calloc(sizeof(CGFloat), numberOfScanLines); 

     // Walk the menubar image from right to left, pixel by pixel 
     for (int scanx = 0; scanx < numberOfScanLines; scanx++) 
     { 

      // Optimization, if we recently found a really good match, bail on the loop and return it 
      if ((presumptiveMatchIdx >= 0) && (scanx > (presumptiveMatchIdx + 5))) { 
       break; 
      } 

      CGFloat xOffset = numberOfScanLines - scanx; 
      CGRect displayRect = CGRectMake(xOffset * scaleFactor, 0, IMG.size.width * scaleFactor, 22. * scaleFactor); 
      CGImageRef displayCrop = CGImageCreateWithImageInRect(displayImg, displayRect); 

      CGContextRef compareCxt = CGBitmapContextCreate(bm_compare, bm_compare_w, bm_compare_h, 8, 1 * bm_compare_w, csK, kCGBitmapAlphaInfoMask&kCGImageAlphaNone); 
      CGContextSetBlendMode(compareCxt, kCGBlendModeCopy); 

      // Draw the image from our menubar 
      CGContextDrawImage(compareCxt, CGRectMake(0,0,IMG.size.width * scaleFactor, 22. * scaleFactor), displayCrop); 

      // Blend mode difference is like an XOR 
      CGContextSetBlendMode(compareCxt, kCGBlendModeDifference); 

      // Draw the test image. Because of blend mode, if we end up with a black image we matched perfectly 
      CGContextDrawImage(compareCxt, CGRectMake(0,0,IMG.size.width * scaleFactor, 22. * scaleFactor), compareImg); 

      CGContextFlush(compareCxt); 

      // Walk through the result image, to determine overall blackness 
      bm_compare_ptr = bm_compare; 
      for (int i = 0; i < bm_compare_w * bm_compare_h; i++) 
      { 
       meanValues[scanx] += (CGFloat)(*bm_compare_ptr); 
       bm_compare_ptr++; 
      } 
      meanValues[scanx] /= (255. * (CGFloat)(bm_compare_w * bm_compare_h)); 

      // If the image is very dark, it matched well. If the average pixel value is < 0.07, we consider this 
      // a presumptive match. Mark it as such, but continue looking to see if there's an even better match. 
      if (meanValues[scanx] < 0.07) { 
       if (meanValues[scanx] < presumptiveMatchMeanVal) { 
        presumptiveMatchMeanVal = meanValues[scanx]; 
        presumptiveMatchIdx = scanx; 
       } 
      } 

      CGImageRelease(displayCrop); 
      CGContextRelease(compareCxt); 

     } 
    } 


    // After we're done scanning the whole menubar (or we bailed because we found a good match), 
    // return the origin point. 
    // If we didn't match well enough, return NSZeroPoint 
    if (presumptiveMatchIdx >= 0) { 
     ret = CGPointMake(CGRectGetMaxX(self.frame), CGRectGetMaxY(self.frame)); 
     ret.x -= (IMG.size.width + presumptiveMatchIdx); 
     ret.y -= 22; 
    } 


    CGImageRelease(displayImg); 
    CGImageRelease(compareImg); 
    CGColorSpaceRelease(csK); 

    if (bm_bar) free(bm_bar); 
    if (bm_compare) free(bm_compare); 
    if (meanValues) free(meanValues); 

    return ret; 
} 

@end 
संबंधित मुद्दे