【问题标题】:Changing NSApplicationIcon across a running application?跨正在运行的应用程序更改 NSApplicationIcon?
【发布时间】:2010-11-26 17:28:04
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: cocoa image icons nsapplication nsalert


    【解决方案1】:

    试试[myImage setName:@"NSApplicationIcon"](在NSApp中设置为应用图标图片后)。

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

    【讨论】:

    • 这不会覆盖已经存在的命名图像:“如果接收者已经以不同的名称注册,则此方法会取消注册另一个名称。如果以aString中指定的名称注册不同的图像, 这个方法什么也不做,返回 NO。”不幸的是,在现有的应用程序图标图像上调用 setName:nil 或 @"" 似乎也没有改变。
    • 另外,setApplicationIconImage: 不会像人们想象的那样替换现有对象,它会修改其中包含的表示。理想情况下,会有一个与图像代表相关的通知,NSImageViews 可以观察并根据需要重绘自己。
    【解决方案2】:

    调动[NSImage imageNamed:] 方法?这种方法至少在 Snow Leopard, 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: 调用,而不是我的做法。

    【讨论】:

    • 已验证这在 OSX10.9 和 10.10 中有效。警告#1 是正确的,所有已经绘制的 NSApplicationIcon 图像都没有被 swizzle 更新,这是有道理的。因此,任何现有图像都必须以编程方式定位和更改。使用 js-ctypes:gist.github.com/Noitidart/3571dbc1b75f28532bc2
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 2014-01-22
    • 2014-09-03
    • 1970-01-01
    • 2015-09-06
    相关资源
    最近更新 更多