【问题标题】:Is there a way of getting a Mac's icon given its model number?有没有办法在给定型号的情况下获取 Mac 的图标?
【发布时间】:2015-11-28 22:59:57
【问题描述】:

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

NSImage *machineIcon = [NSImage imageNamed:NSImageNameComputer];

但是如果只给出型号,是否可以获得图标?比如MacBookPro11,3?

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

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

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

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

我曾考虑使用MCNearbyServiceAdvertiser 中的discoveryInfo 来传输设备广告的图标,但您无法使用discoveryInfo 传输那么多数据。它仅适用于少量文本。所以我决定只传输机器的型号。我希望将机器的型号解析为另一侧的图标。有点像AirDrop 的做法。

【问题讨论】:

    标签: macos cocoa multipeer-connectivity airdrop


    【解决方案1】:
    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 不安全

    将私有框架 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 彩色版本的新尿路感染 com.apple.macbook-视网膜-银 com.apple.device-model-code MacBook8,1@ECOLOR=225,225,223

    com.apple.macbook-retina-gold com.apple.device-model-code MacBook8,1@ECOLOR=235,215,191

    com.apple.macbook-retina-space-gray com.apple.device-model-code MacBook8,1@ECOLOR=155,158,159 MacBook8,1@ECOLOR=157,157,160

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

    如何获取型号/标识符(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];
    

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

    【讨论】:

    • 嗯...问题是我需要能够提交到 Mac App Store。所以这对我来说是个问题。
    【解决方案2】:

    这是 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

    【讨论】:

    • 这很棒。但我绝对需要能够提交到 Mac App Store。我想知道我最好的选择是否只是将该文件复制到我的应用程序包以及所有图标中并像这样分发它?尽管使用了他们的图标,但苹果可能会以侵犯版权为由拒绝它。如果出现新型号,我可以只为机器类型提供一个通用图标。例如。从模型中解析出“MacBook”或“MacPro”并选择一个匹配的图标。
    • 我还想到 AirDrop 实际上并没有使用机器的图标。它使用登录帐户的图标。我得做更多调查。
    • 您正在浪费时间了解细节。先发货吧。顺便说一句,不错的解决方案(但再次需要在沙盒应用程序包之外访问才能获取图标)。
    • 嗯,我测试过的每个模型 ID 都没有这样的“hardwareImageName”键。运行 10.11.6 (15G31)
    • 免责声明:此系统 plist 不再包含此信息,Apple 更改了它,我现在不知道在哪里可以找到它。此答案仅适用于 El Capitan 之前的 OS X 版本。
    【解决方案3】:

    可以在不使用私有框架的情况下以编程方式将模型标识符转换为图像。此代码至少适用于 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 可能返回CFStringRefCFArrayRefCFStringRef。代码 sn-p 假定永远只会返回 CFStringRef

    适应沙盒

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-17
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-03
      相关资源
      最近更新 更多