【问题标题】:How to get [UIImage imageWithContentsOfFile:] and High Res Images working如何让 [UIImage imageWithContentsOfFile:] 和高分辨率图像工作
【发布时间】:2011-04-13 23:30:49
【问题描述】:

正如许多人抱怨的那样,在用于 Retina 显示器的 Apple SDK 中似乎存在一个错误,并且 imageWithContentsOfFile 实际上不会自动加载 2x 图像。

我偶然发现了一篇不错的帖子,如何制作一个检测 UIScreen 比例因子并正确加载低分辨率或高分辨率图像 (http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/) 的函数,但该解决方案加载了 2x 图像并且仍然具有图像设置为 1.0,这将导致 2x 图像缩放 2 倍(因此,比它看起来要大 4 倍)

imageNamed 似乎可以准确地加载低分辨率和高分辨率图像,但我没有选择。

是否有人有不使用自动加载 imageNamed 或 imageWithContentsOfFile 来加载低/高分辨率图像的解决方案? (或最终解决如何使 imageWithContentsOfFile 正常工作)

【问题讨论】:

    标签: objective-c uiimage ios4 high-resolution imagenamed


    【解决方案1】:

    好的,Michael 在这里找到的实际解决方案: http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/

    他发现 UIImage 有方法“initWithCGImage”,它也接受一个比例因子作为输入(我猜你可以设置自己的比例因子的唯一方法)

    [UIImage initWithCGImage:scale:orientation:]
    

    这似乎很好用,您可以自定义加载高分辨率图像并将比例因子设置为 2.0

    imageWithContentsOfFile 的问题在于,由于它目前无法正常工作,因此即使它已修复,我们也无法信任它(因为某些用户的设备上仍会使用较旧的 iOS)

    【讨论】:

    • 如果您支持 iOS 4.1 或更高版本,请参阅“bioffe”的答案。简单得多。
    【解决方案2】:

    我们刚刚在工作中遇到了这个问题。 这是我的解决方法,似乎站得住脚:

    NSString *imgFile = ...path to your file;
    NSData *imgData = [[NSData alloc] initWithContentsOfFile:imgFile];
    UIImage *img = [[UIImage alloc] initWithData:imgData];
    

    【讨论】:

      【解决方案3】:

      imageWithContentsOfFile 从 iOS 4.1 及更高版本开始正常工作(考虑具有正确比例的@2x 图像)。

      【讨论】:

      • 尽管文档说,imageWithContentsOfFile: 不适用于绝对路径。在 iOS 5.1 上测试
      • 对我来说,它不适用于缓存文件夹中图像的绝对路径。将进一步调查,也许这是一个糟糕的测试。
      • 不适用于 Retina iPad 的 iOS 5.1 sim。使用 [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image.png" ofType:nil]]
      • @Jarson 我使用它的方式几乎相同,但不同 [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"]]
      • +1 我可以确认 @2x 加载 确实 对我使用 imageWithContentsOfFile 在 iOS5.1 和 iOS6 上使用视网膜模拟器(iPhone 和 iPad)。如果有人正在检查模拟器,请确保您运行的是 Retina 版本的模拟器!
      【解决方案4】:

      增强 Lisa Rossellis 的答案以将视网膜图像保持在所需大小(而不是按比例放大):

      NSString *imagePath = ...Path to your image
      UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imagePath] scale:[UIScreen mainScreen].scale];
      

      【讨论】:

        【解决方案5】:

        我已经为这个问题开发了一个临时解决方法。 它使用方法调配来替换 UIImage 的“imageWithContentsOfFile:”方法的行为。 它在视网膜前/后的 iPhone/iPod 上运行良好。 不确定 iPad。

        希望对您有所帮助。

        #import </usr/include/objc/objc-class.h>
        
        @implementation NSString(LoadHighDef)
        
        /** If self is the path to an image, returns the nominal path to the high-res variant of that image */
        -(NSString*) stringByInsertingHighResPathModifier {
        
             NSString *path = [self stringByDeletingPathExtension];
        
             // We determine whether a device modifier is present, and in case it is, where is 
             // the "split position" at which the "@2x" token is to be added
             NSArray  *deviceModifiers = [NSArray arrayWithObjects:@"~iphone", @"~ipad", nil];
             NSInteger splitIdx = [path length];
             for (NSString *modifier in deviceModifiers) {
                  if ([path hasSuffix:modifier]) {
                       splitIdx -= [modifier length];
                       break;
                  }
             }
        
             // We insert the "@2x" token in the string at the proper position; if no 
             // device modifier is present the token is added at the end of the string
             NSString *highDefPath = [NSString stringWithFormat:@"%@@2x%@",[path substringToIndex:splitIdx], [path substringFromIndex:splitIdx]];
        
             // We possibly add the extension, if there is any extension at all
             NSString *ext = [self pathExtension];
             return [ext length]>0? [highDefPath stringByAppendingPathExtension:ext] : highDefPath;
        }
        
        @end
        
        @implementation UIImage (LoadHighDef)
        
        /* Upon loading this category, the implementation of "imageWithContentsOfFile:" is exchanged with the implementation
         * of our custom "imageWithContentsOfFile_custom:" method, whereby we replace and fix the behavior of the system selector. */
        +(void)load {
             Method originalMethod    = class_getClassMethod([UIImage class], @selector(imageWithContentsOfFile:));
             Method replacementMethod = class_getClassMethod([UIImage class], @selector(imageWithContentsOfFile_custom:));
             method_exchangeImplementations(replacementMethod, originalMethod);
        }
        
        /** This method works just like the system "imageWithContentsOfFile:", but it loads the high-res version of the image 
         *  instead of the default one in case the device's screen is high-res and the high-res variant of the image is present.
         *
         *  We assume that the original "imageWithContentsOfFile:" implementation properly sets the "scale" factor upon 
         *  loading a "@2x" image . (this is its behavior as of OS 4.0.1).
         *
         *  Note: The "imageWithContentsOfFile_custom:" invocations in this code are not recursive calls by virtue of 
         *  method swizzling. In fact, the original UIImage implementation of "imageWithContentsOfFile:" gets called.
         */
        
        + (UIImage*) imageWithContentsOfFile_custom:(NSString*)imgName {
        
             // If high-res is supported by the device...
             UIScreen *screen = [UIScreen mainScreen];
             if ([screen respondsToSelector:@selector(scale)] && [screen scale]>=2.0) {
        
                  // then we look for the high-res version of the image first
                  UIImage  *hiDefImg = [UIImage imageWithContentsOfFile_custom:[imgName stringByInsertingHighResPathModifier]];
        
                  // If such high-res version exists, we return it
                  // The scale factor will be correctly set because once you give imageWithContentsOfFile:
                  // the full hi-res path it properly takes it into account 
                  if (hiDefImg!=nil)
                       return hiDefImg;
             }
        
             // If the device does not support high-res of it does but there is
             // no high-res variant of imgName, we return the base version
             return [UIImage imageWithContentsOfFile_custom:imgName];
        }
        
        @end
        

        【讨论】:

        • 嗨,Marco,正如问题中所述 - 文件的名称根本不是问题,而是图像本身的比例因子。
        • 嗨,Ican,我的代码正确设置了比例因子(在 OS 4.0.1 和 4.1 的模拟器中测试)。关键是,虽然 imageWithContentsOfFile: 不会自动找到“@2x”变体,但正如您所说,一旦您指示它直接获取“@2x”文件(如在我的 swizzled 方法中),它就会正确设置比例因子。只需将我的两个类别放在代码中它们被编译的地方,然后尝试使用 imageWithContentsOfFile: 就像它没有被破坏一样
        【解决方案6】:

        如果您指定绝对路径,[UIImage imageWithContentsOfFile:] 不会加载 @2x 图形。

        这里有一个解决方案:

        - (UIImage *)loadRetinaImageIfAvailable:(NSString *)path {
        
            NSString *retinaPath = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@@2x.%@", [[path lastPathComponent] stringByDeletingPathExtension], [path pathExtension]]];
        
            if( [UIScreen mainScreen].scale == 2.0 && [[NSFileManager defaultManager] fileExistsAtPath:retinaPath] == YES) 
                return [[[UIImage alloc] initWithCGImage:[[UIImage imageWithData:[NSData dataWithContentsOfFile:retinaPath]] CGImage] scale:2.0 orientation:UIImageOrientationUp] autorelease];
            else
                return [UIImage imageWithContentsOfFile:path];
        }
        

        感谢 Christof Dorner 他的简单解决方案(我在此处修改并粘贴)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-04-07
          • 2014-11-20
          • 1970-01-01
          • 2017-12-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多