¿Es posible obtener el marco de un NSStatusItem
después de haberlo agregado a la barra de estado en Cocoa? Cuando se inicia mi aplicación, agrego un elemento a la barra de estado del sistema, y me gustaría saber dónde está posicionado, es posible.Cómo obtener el marco para NSStatusItem
Respuesta
Si ha establecido una vista personalizada en el elemento de estado:
NSRect statusRect = [[statusItem view] frame];
NSLog(@"%@", [NSString stringWithFormat:@"%.1fx%.1f",statusRect.size.width, statusRect.size.height]);
lo contrario, no creo que sea posible el uso de las APIs disponibles y documentados.
Editar: Comentarios incorporados.
se puede hackear la Ivar ventana como esta:
@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
Esto es útil para elementos de estado sin una vista personalizada.
probado en León
El siguiente parece funcionar - He visto soluciones similares para las aplicaciones de iOS y supuestamente permitir que la sumisión a la tienda de aplicaciones, ya que todavía está utilizando métodos estándar de SDK.
NSRect frame = [[statusBarItem valueForKey:@"window"] frame];
Esto funciona, y es útil cuando no ha configurado una vista personalizada para el elemento de estado. – Fabian
Esto supone que NSStatusItem (suponiendo que eso es lo que 'statusBarItem' es) tiene algo que KVC puede usar como propiedad' window'. Eso no está garantizado para ser el caso. Obtendrá una excepción (no compatible con KVC para esta clave) si/cuando eso desaparezca. Tampoco contaría con esta revisión pasajera de la App Store; si no lo hacen ya, algún día podrán comenzar a verificar cómo usas KVC (buscando usos como este, donde accedes a métodos privados/ivars). –
¿Se trata de una llamada de API privada que podría hacer que te rechacen del proceso de revisión de la tienda de aplicaciones? –
Con 10.10, NSStatusItem
tiene una propiedad button
que se utiliza para obtener la posición de elemento de estado sin fijar una vista personalizada.
NSStatusBarButton *statusBarButton = [myStatusItem button];
NSRect rectInWindow = [statusBarButton convertRect:[statusBarButton bounds] toView:nil];
NSRect screenRect = [[statusBarButton window] convertRectToScreen:rectInWindow];
NSLog(@"%@", NSStringFromRect(screenRect));
Consejo impresionante, pero 10.10 aún no es popular. :( –
Esto es posible sin API privada. Aquí hay una categoría para NSScreen. Utiliza el análisis de imagen para ubicar la imagen del elemento de estado en la barra de menú. Afortunadamente, las computadoras son realmente rápidas. :)
Siempre que sepa cómo se ve la imagen del elemento de estado, y puede pasarlo como un NSImage, este método debería encontrarlo.
Funciona tanto para el modo oscuro como para el modo normal. Tenga en cuenta que la imagen que pase debe ser negra. Las imágenes en color probablemente no funcionen tan bien.
@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
- 1. texto borroso en NSStatusItem
- 2. JDesktopPane: cómo obtener el marco activo
- 3. Ocultar NSMenu programáticamente de NSStatusItem
- 4. informándome cuando NSStatusItem está oculto
- 5. Cómo obtener el sql generado por el marco de entidad
- 6. NSStatusItem y imagen resaltada en blanco
- 7. resaltado NSStatusItem con una cadena con atributos
- 8. ¿Se puede reducir un NSStatusItem para que quepa?
- 9. ¿Cómo obtener el marco de video del AVPlayer?
- 10. cómo obtener ventaja del marco sin estado
- 11. ¿Cómo integrar el marco XMPP para iPhone?
- 12. ¿Cómo obtener el marco de imagen mostrado desde UIImageView?
- 13. ¿Cómo utilizar el marco del núcleo bluetooth obtener datos?
- 14. Cocoa: haga clic con el botón secundario en NSStatusItem
- 15. NSStatusItem aparece brevemente en el inicio, pero desaparece rápidamente
- 16. ¿Cómo abrir una ventana al hacer clic en NSStatusItem?
- 17. Cómo generar JSON programáticamente utilizando el marco JSON para iPhone
- 18. Cómo incluir el marco SDWebImage
- 19. Java - Xuggle - Mejor método para obtener un marco
- 20. En Python, ¿cómo obtengo el marco actual?
- 21. Cómo establecer el marco cell.imageview
- 22. cómo obtener un solo valor usando marco de la entidad
- 23. Cómo escribir comprobaciones/validación personalizadas para el marco de juego
- 24. Obtener el marco del teclado de forma dinámica
- 25. Cómo usar el juego! marco para desarrollar servicios web?
- 26. ¿Cómo funciona el marco BDD "specs" para Scala?
- 27. ¿Cómo modificar el marco para admitir muti-skin?
- 28. Marco de Java para el sistema distribuido
- 29. Recomendar el marco web para Clojure
- 30. Marco para escribir el servicio web PHP
No me funcionó. Un NSStatusItem no parece tener una vista predeterminada, por lo que [statusItem view] devuelve null. – blutfink
Como dice el documento, "Devuelve la vista personalizada que se muestra en la posición del receptor en la barra de estado", no la vista de NSStatusItem. –
Esto solo funciona si ha configurado una vista personalizada en el elemento de estado. – Fabian