有没有办法根据型号获得 Mac 的图标?

Is there a way of getting a Mac's icon given its model number?

我知道您可以使用以下代码从 cocoa 获取当前机器的图标:

NSImage *machineIcon = [NSImage imageNamed:NSImageNameComputer];

但是如果只给出型号就可以得到图标吗?比如MacBookPro11,3?

我需要这个的原因是因为我正在使用 MultiPeer Connectivity 浏览网络上我想连接的设备。但我想在自定义浏览器视图中显示这些设备的图标。

我知道 OS X 在以下文件夹中包含几乎所有设备的所有图标:

/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/

但我想知道如何从我的应用程序中访问它们:

我想过用MCNearbyServiceAdvertiserdiscoveryInfo传输一个设备广告的图标,但是用discoveryInfo传输不了那么多数据。它专为少量文本而设计。所以我决定只传输机器的型号。我希望将机器的型号解析为另一侧的图标。有点像 AirDrop 是怎么做的。

  1. Mac App Store 安全

手动将模型标识符映射到图标名称,然后使用

[[NSWorkspace sharedWorkspace] iconForFileType:@"com.apple.macbookair"];

 [NSImage imageNamed:NSImageNameComputer]

如果您需要比 imageNamed 提供的分辨率更高的分辨率,请使用

  OSType code = UTGetOSTypeFromString((CFStringRef)CFSTR("root"));
  NSImage *computer = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(code)];

其中 "root" 字符串来自 IconsCore.h 头文件 (kComputer)。

复制此 plist 以获取标识符(不要从应用沙箱访问它)

/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist

  1. Mac App Store 不安全

Link 私有框架 SPSupport.Framework 与你的二进制文件 添加 FrameWork 搜索路径变量

$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks

将以下界面添加到您的项目中

#import <Cocoa/Cocoa.h>

@interface SPDocument : NSDocument

- (NSImage *)modelIcon;
- (id)computerName;
- (id)serialNumber;
- (id)modelName;

@end

调用您的代码:

  SPDocument *document = [[SPDocument alloc] init];
  NSImage *icon = [document modelIcon];
  1. 最难的方法

用这个私有函数找出 CoreFoundation 的舞蹈(这段代码是说明,找到正确的类型、参数数量并正确发布)

  output = _LSCreateDeviceTypeIdentifierWithModelCode((CFStringRef)@"MacBookPro6,2");
  NSImage *image = [[NSWorkspace sharedWorkspace] iconForFileType: output];

编辑: 我刚刚意识到您需要选项编号 1,3(给定型号的图标)。 GL 与此作斗争。

EDIT2 添加了方法 3。更改顺序并添加到编号 1 下。

EDIT3 彩色版本的新 UTI com.apple.macbook-视网膜银 com.apple.device-型号代码 MacBook8,1@ECOLOR=225,225,223

com.apple.macbook-视网膜金 com.apple.device-型号代码 MacBook8,1@ECOLOR=235,215,191

com.apple.macbook-视网膜-space-灰色 com.apple.device-型号代码 MacBook8,1@ECOLOR=155,158,159 MacBook8,1@ECOLOR=157,157,160

NSImage *image =[[NSWorkspace sharedWorkspace] iconForFileType:@"com.apple.macbook-retina-gold"];

如何获取模型number/identifier(sysctl hw.model被system_profiler取代)?

NSPipe *outputPipe = [NSPipe pipe];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/usr/sbin/system_profiler"];
[task setArguments:@[@"SPHardwareDataType"]];
[task setStandardOutput:outputPipe];
[task launch];
[task waitUntilExit];
NSData *outputData = [[outputPipe fileHandleForReading] readDataToEndOfFile];
NSString *hardware = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];

并解析模型标识符或您的属性列表序列化

这是 Swift 中的解决方案,但它使用私有 API 因此请记住,它可能会受到未记录的更改和 App Store 拒绝。

struct MachineAttributes {

    let privateFrameworksURL = "/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist"

    var model: String?

    var attributes: [String:AnyObject]?

    var iconPath: String?

    init() {
        self.model = getModel()
        self.attributes = getAttributes()
        self.iconPath = getIconPath()
    }

    init(model: String) {
        self.model = model
        self.attributes = getAttributes()
        self.iconPath = getIconPath()
    }

    private func getModel() -> String? {
        let service: io_service_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"))
        let cfstr = "model" as CFString
        if let model = IORegistryEntryCreateCFProperty(service, cfstr, kCFAllocatorDefault, 0).takeUnretainedValue() as? NSData {
            if let nsstr = NSString(CString: UnsafePointer<Int8>(model.bytes), encoding: NSUTF8StringEncoding) {
                return String(nsstr)
            }
        }
        return nil
    }

    private func getAttributes() -> [String:AnyObject]? {
        if let dict = NSDictionary(contentsOfFile: privateFrameworksURL) as? [String:AnyObject],
            let id = model,
            let result = dict[id] as? [String:AnyObject] {
                return result
        }
        return nil
    }

    private func getIconPath() -> String? {
        if let attr = self.attributes, let path = attr["hardwareImageName"] as? String {
            return path
        }
        return nil
    }

}

如果您不知道型号:

let myMachine = MachineAttributes()
if let model = myMachine.model {
    print(model)
}
if let iconPath = myMachine.iconPath {
    print(iconPath)
}

MacBookPro9,2

/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/com.apple.macbookpro-15-unibody.icns

如果您知道该型号或想使用其他型号:

let myNamedMachine = MachineAttributes(model: "iMac14,2")
if let iconPath = myNamedMachine.iconPath {
    print(iconPath)
}

/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/com.apple.imac-unibody-27-no-optical.icns

可以在不使用私有框架的情况下以编程方式将模型标识符转换为图像。此代码至少适用于 OS X 10.4 及更高版本。

 + (NSImage*)imageForMachineModel:(NSString*)machineModel
 {
     NSImage* image = nil;

     NSString* uti = NULL;
     if ((machineModel != nil) &&
         ([machineModel length] > 0))
     {
         NSString* fixedModel = [NSString stringWithUTF8String:machineModel.UTF8String];
         uti = (__bridge_transfer NSString*) UTTypeCreatePreferredIdentifierForTag(CFSTR("com.apple.device-model-code"),(__bridge CFStringRef)fixedModel,NULL);
     }
     else
     {
         // Default to a generic Mac image for null
         uti = (__bridge_transfer NSString*) CFStringCreateCopy(kCFAllocatorDefault,CFSTR("com.apple.mac"));
     }

     if (uti != NULL)
     {
         CFDictionaryRef utiDecl = UTTypeCopyDeclaration((__bridge CFStringRef)(uti));
         if (utiDecl != NULL)
         {
             CFStringRef iconFileName = CFDictionaryGetValue(utiDecl,CFSTR("UTTypeIconFile"));
             if (iconFileName == NULL)
             {
                 while((iconFileName == NULL) &&
                       (utiDecl != NULL) &&
                       (uti != NULL))
                 {
                     // BUG: macOS 10.12 may return string or array of strings; unchecked in this implementation!
                     uti = NULL;
                     uti = CFDictionaryGetValue(utiDecl,CFSTR("UTTypeConformsTo"));
                     if (uti != NULL)
                     {
                         CFRelease(utiDecl);
                         utiDecl = NULL;
                         utiDecl = UTTypeCopyDeclaration((__bridge CFStringRef)(uti));
                         if (utiDecl != NULL)
                         {
                             iconFileName = CFDictionaryGetValue(utiDecl,CFSTR("UTTypeIconFile"));
                         }
                     }
                 }
             }

             if (iconFileName != NULL)
             {
                 CFURLRef bundleURL = UTTypeCopyDeclaringBundleURL((__bridge CFStringRef)(uti));
                 if (bundleURL != NULL)
                 {
                     NSBundle* bundle = [NSBundle bundleWithPath:[(__bridge NSURL*)bundleURL path]];
                     if (bundle != nil)
                     {
                         NSString* iconPath = [bundle pathForResource:(__bridge NSString*)iconFileName ofType:nil];
                         if (iconPath != nil)
                         {
                             image = [[NSImage alloc] initWithContentsOfFile:iconPath];
                         }
                     }
                     CFRelease(bundleURL);
                 }
             }

             if (utiDecl != NULL)
             {
                 CFRelease(utiDecl);
             }
         }
     }

     if (image == nil)
     {
         // Do something sensible if no image found
     }

     return image;
 }

请注意,macOS 10.12 beta 6 目前存在一个错误 rdr://27883672,该错误会在 UTTypeCopyDeclaration 中崩溃。这是因为 UTTypeConformsTo 可能 return 是 CFStringRefCFStringRefCFArrayRef。代码片段假设只有 CFStringRef 会被 returned.

适应沙盒

要将此代码与沙盒一起使用,请避免直接加载图标文件。而是从 utiDecl 获取 UTTypeIdentifier 并将其传递给 NSWorkspaceiconForFileType.