3

我想调整在所有警报中自动显示的 NSApplicationIcon 图像,使其与应用程序包中的图像不同。

我知道可以使用 [NSApplication setApplicationIconImage:] 设置停靠栏图标——但这只会影响停靠栏,不会影响其他任何内容。

有时我可以解决这个问题:我有一个 NSAlert *,我可以调用 setIcon: 来显示我的备用图像。

不幸的是,我有很多带有 NSImageView 和 NSApplicationIcon 的 nib,我想影响它们,创建插座并输入代码来更改图标会很麻烦。对于我通过 BeginAlert... 类型调用提出的任何警报(它不会给 NSAlert 对象提供垃圾),我完全不走运。

任何人都可以想出一种合理的方式来全局(对于正在运行的应用程序的生命周期)用我自己的图像覆盖 AppKit 使用的 NSApplicationIcon,这样我就可以替换 100% 的警报(并使我的代码更简单) ?

4

2 回答 2

2

调换[NSImage imageNamed:]方法?这种方法至少适用于雪豹,YMMV。

在一个NSImage类别中:

@implementation NSImage (Magic)

+ (void)load {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // have to call imageNamed: once prior to swizzling to avoid infinite loop
    [[NSApplication sharedApplication] applicationIconImage];

    // swizzle!
    NSError *error = nil;

    if (![NSImage jr_swizzleClassMethod:@selector(imageNamed:) withClassMethod:@selector(_sensible_imageNamed:) error:&error])
        NSLog(@"couldn't swizzle imageNamed: application icons will not update: %@", error);

    [pool release];
}


+ (id)_sensible_imageNamed:(NSString *)name {
    if ([name isEqualToString:@"NSApplicationIcon"])
        return [[NSApplication sharedApplication] applicationIconImage];

    return [self _sensible_imageNamed:name];
}

@end

有了这个破解(未经测试,只是写了它)jr_swizzleClassMethod:...实现:

+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {
#if OBJC_API_VERSION >= 2
    Method origMethod = class_getClassMethod(self, origSel_);
    if (!origMethod) {
        SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);
        return NO;
    }

    Method altMethod = class_getClassMethod(self, altSel_);
    if (!altMethod) {
        SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);
        return NO;
    }

    id metaClass = objc_getMetaClass(class_getName(self));

    class_addMethod(metaClass,
                    origSel_,
                    class_getMethodImplementation(metaClass, origSel_),
                    method_getTypeEncoding(origMethod));
    class_addMethod(metaClass,
                    altSel_,
                    class_getMethodImplementation(metaClass, altSel_),
                    method_getTypeEncoding(altMethod));

    method_exchangeImplementations(class_getClassMethod(self, origSel_), class_getClassMethod(self, altSel_));
    return YES;
#else
    assert(0);
    return NO;
#endif
}

那么,这个方法来说明一点:

- (void)doMagic:(id)sender {
    static int i = 0;

    i = (i+1) % 2;

    if (i)
        [[NSApplication sharedApplication] setApplicationIconImage:[NSImage imageNamed:NSImageNameBonjour]];
    else
        [[NSApplication sharedApplication] setApplicationIconImage:[NSImage imageNamed:NSImageNameDotMac]];

    // any pre-populated image views have to be set to nil first, otherwise their icon won't change
    // [imageView setImage:nil];
    // [imageView setImage:[NSImage imageNamed:NSImageNameApplicationIcon]];

    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
    [alert setMessageText:@"Shazam!"];
    [alert runModal];
}

几个警告:

  1. 任何已经创建的图像视图都必须setImage:调用两次,如上所示以注册图像更改。不知道为什么。
  2. 可能有更好的方法来强制初始imageNamed:调用,而@"NSApplicationIcon"不是我已经完成的方法。
于 2009-09-09T07:03:59.853 回答
1

尝试[myImage setName:@"NSApplicationIcon"](在将其设置为 NSApp 中的应用程序图标图像后)。

注意:在 10.6 及更高版本上,您可以并且应该使用NSImageNameApplicationIcon字符串 literal 来代替@"NSApplicationIcon"

于 2009-09-09T10:48:22.513 回答