2009-09-09 40 views
1

我想調整在所有警報中自動顯示的NSApplicationIcon圖像與應用程序包中的內容不同。在正在運行的應用程序中更改NSApplicationIcon?

我知道可以使用[NSApplication setApplicationIconImage:]設置停靠欄圖標 - 但這隻影響停靠欄,沒有別的。

我可以在某些時候解決此問題:我有一個NSAlert *,我可以調用setIcon:顯示我的備用圖像。

不幸的是,我有很多的NSImageView與NSApplicationIcon,我想影響很多的筆尖,並且創建插座並放入代碼來更改圖標會很麻煩。對於任何提醒我用BeginAlert ...類型的調用(這不會讓NSAlert對象混淆),我完全沒有運氣。

任何人都可以想出一個合理的方法來全局(對於運行中的應用程序的生命)覆蓋由AppKit使用的NSApplicationIcon和我自己的圖像,以便我可以取代100%的警報(並使我的代碼更簡單)?

回答

1

Swizzle的[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. 可能有一個更好的方法來強制與@"NSApplicationIcon"的初始imageNamed:呼叫比我已經做到了。
+0

已驗證,它適用於OSX10.9和10.10。警告#1成立,所有已經繪製的NSApplicationIcon圖像都沒有通過swizzle進行更新,這是有道理的。因此,任何現有的圖像必須以編程爲目標並進行更改。使用js-ctypes:https://gist.github.com/Noitidart/3571dbc1b75f28532bc2 – Noitidart 2015-03-01 00:26:57

0

嘗試[myImage setName:@"NSApplicationIcon"](將其設置爲NSApp中的應用程序圖標圖像之後)。

注意:在10.6及更高版本中,您可以並應該使用NSImageNameApplicationIcon而不是字符串文字@"NSApplicationIcon"

+1

這不會覆蓋已存在的已命名圖像:「如果接收方已經以不同的名稱註冊,則此方法取消註冊其他名稱。一個不同的圖像在aString中指定的名稱下注冊,此方法不做任何操作並返回NO。「不幸的是,在預先存在的應用程序圖標圖像上調用setName:nil或@「」並不會改變事物。 – 2009-09-09 15:33:10

+1

此外,setApplicationIconImage:不會像人們想象的那樣替換現有對象,它會修改其中包含的表示。 理想情況下,會有一個與圖像代碼更改相關的通知,NSImageViews可以根據需要觀察和重繪自己。 – 2009-09-09 15:58:21

相關問題