我正在使用Xcode的屏幕保护程序模板为OS X开发屏幕保护程序。通过检查系统屏幕保护程序的包装内容,我发现屏幕保护程序的“系统偏好设置”列表中使用的缩略图来自屏幕保护程序包中的两个文件:
thumbnail.png (90x58)
[email protected] (180x116)
我已经创建了两个这样大小的图像,并将它们放置在屏幕保护程序包中。但是,我的视网膜屏幕上的“系统偏好设置”面板似乎正在加载非视网膜资产。这是[email protected]
图像的QuickLook预览旁边的“系统偏好设置”面板的屏幕截图:
我没主意了。有谁知道这可能是什么原因以及如何阻止它?我尝试过的事情:
thumbnail.png
-结果相同。如果检查系统屏幕保护程序的软件包,则会发现这两个缩略图文件未报告Finder中的尺寸。而且,如果您使用Sketch打开它们,则它们在该应用中均显示为90x58。 (尽管Photoshop将@ 2x资产显示为180x116)。系统屏幕保护程序的缩略图已应用了光泽效果,而我的缩略图自动获得了该效果,即使图像资产不包含该效果也是如此。
我开始认为面板加载/绘制这些图像的方式有些混乱。也许有人知道我不知道的东西?
我不确定这是否符合解决方案 ...但是在花了很多时间摆弄图像属性并研究了苹果的屏幕保护程序后,我最终遇到了以下黑客,事实证明这很漂亮有效。我不知道这是否违反任何Apple政策,但对我有用™
<<我们将利用屏幕保护程序作为插件加载到
系统偏好设置
应用程序的事实。-(void)initThumbnail
{
// Screen savers are loaded as plugins so their main bundle is not
// this one, but the host bundle. We need to use the name directly.
NSBundle *bundle = [NSBundle bundleWithIdentifier:@"com.example.ScreenSaver"];
// We're gonna use the title of the screen saver as seen in the list
// of the "Desktop & Screen Saver" window. This is localized name of
// the bundle.
NSString *saverTitle = bundle.localizedBundleName;
// And this is the actual "thumnail.png" resource, with full Retina
// quality (if provided). However, without any customary frame.
NSImage *saverThumbnail = [bundle imageForResource:@"thumbnail"];
// Locate our top-level window, walk through all subviews in its
// content view, and find one with a certain private class which
// has a title that matches ours. Then switch thumbnails there.
NSWindow *topWindow = self.window;
while (topWindow.parentWindow != nil) {
topWindow = topWindow.parentWindow;
}
for (NSView *view in topWindow.contentView.subviewsRecursive) {
if ([view.className isEqualToString:@"IndividualSaverIconView"]) {
NSString *title = [view performSelector:@selector(title)];
if ([title isEqualToString:saverTitle]) {
[view performSelector:@selector(setImage:)
withObject:saverThumbnail];
// This hack was sponsored by NeXTSTEP and Objective-C.
return;
}
}
}
}
这使用以下帮助程序类别,使代码更易读:
@implementation NSBundle (Name)
- (NSString *)localizedBundleName
{
NSString *bundleNameKey = (NSString *)kCFBundleNameKey;
NSString *name;
// First look for the localized version...
if ((name = self.localizedInfoDictionary[bundleNameKey])) {
return name;
}
// ...if there is no localization then use the common one
if ((name = self.infoDictionary[bundleNameKey])) {
return name;
}
return nil;
}
@end
@implementation NSView (Subviews)
- (NSArray<NSView *> *)subviewsRecursive
{
// We keep adding objects to this array until we exhaust it
NSMutableArray<NSView *> *subviews = self.subviews.mutableCopy;
for (NSUInteger i = 0; i < subviews.count; i++) {
[subviews addObjectsFromArray:[[subviews objectAtIndex:i] subviews]];
}
return subviews;
}
@end
请注意,您在无法
initWithFrame:isPreview:
中调用此方法,因为此时屏幕保护程序尚未插入视图层次结构中。我发现hasConfigureSheet
是个好地方,只需确保只做一次即可。也请注意,缩略图不会自动获得此光泽框。系统屏幕保护程序已内置,您也应该这样做。