Reputation: 1512
I'd like to adjust the NSApplicationIcon image that gets shown automatically in all alerts to be something different than what is in the app bundle.
I know that it's possible to set the dock icon with [NSApplication setApplicationIconImage:] -- but this only affects the dock, and nothing else.
I'm able to work around this issue some of the time: I have an NSAlert *, I can call setIcon: to display my alternate image.
Unfortunately, I have a lot of nibs that have NSImageView's with NSApplicationIcon, that I would like to affect, and it would be a hassle to create outlets and put in code to change the icon. And for any alerts that I'm bringing up with the BeginAlert... type calls (which don't give an NSAlert object to muck with), I'm completely out of luck.
Can anybody think of a reasonable way to globally (for the life of a running application) override the NSApplicationIcon that is used by AppKit, with my own image, so that I can get 100% of the alerts replaced (and make my code simpler)?
Upvotes: 3
Views: 2143
Reputation: 96373
Try [myImage setName:@"NSApplicationIcon"]
(after setting it as the application icon image in NSApp).
Note: On 10.6 and later, you can and should use NSImageNameApplicationIcon
instead of the string literal @"NSApplicationIcon"
.
Upvotes: 1
Reputation: 8823
Swizzle the [NSImage imageNamed:]
method? This method works at least on Snow Leopard, YMMV.
In an NSImage
category:
@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
With this hacked up (untested, just wrote it) jr_swizzleClassMethod:...
implementation:
+ (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
}
Then, this method to illustrate the point:
- (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];
}
A couple of caveats:
setImage:
called twice, as seen above to register the image changing. Don't know why.imageNamed:
call with @"NSApplicationIcon"
than how I've done it.Upvotes: 2