如何获取[UIImage imageWithContentsOfFile:]和高分辨率图像工作

时间:2010-09-17 23:55:08

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

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

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

imageNamed似乎可以准确地加载低分辨率和高分辨率图像,但对我来说不是一个选择。

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

6 个答案:

答案 0 :(得分:13)

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

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

[UIImage initWithCGImage:scale:orientation:]

这看起来效果很好,您可以自定义加载高分辨率图像并设置比例因子为2.0

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

答案 1 :(得分:11)

我们刚刚在这里遇到了这个问题。 这是我的解决方案似乎有用水:

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

答案 2 :(得分:9)

imageWithContentsOfFile正常工作(考虑到正确比例的@ 2x图像),从iOS 4.1开始。

答案 3 :(得分: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

答案 4 :(得分:5)

增强Lisa Rossellis的答案,将视网膜图像保持在所需的大小(不缩放):

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

答案 5 :(得分:3)

如果指定绝对路径,则

[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