如何让 [UIImage imageWithContentsOfFile:] 和高分辨率图像正常工作

发布于 2024-09-19 12:19:33 字数 615 浏览 5 评论 0原文

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

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

imageNamed 似乎准确地加载了低值和高分辨率图像,但对我来说没有选择。

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

As many people are complaining it seems that in the Apple SDK for the Retina Display there's a bug and imageWithContentsOfFile actually does not automatically load the 2x images.

I've stumbled into a nice post how to make a function which detects UIScreen scale factor and properly loads low or high res images ( http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/ ), but the solution loads a 2x image and still has the scale factor of the image set to 1.0 and this results to a 2x images scaled 2 times (so, 4 times bigger than what it has to look like)

imageNamed seems to accurately load low and high res images, but is no option for me.

Does anybody have a solution for loading low/high res images not using the automatic loading of imageNamed or imageWithContentsOfFile ? (Or eventually solution how to make imageWithContentsOfFile work correct)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

围归者 2024-09-26 12:19:53

如果指定绝对路径,[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 对于他的简单解决方案(我修改并粘贴在这里)。

[UIImage imageWithContentsOfFile:] doesn't load @2x graphics if you specify an absolute path.

Here is a solution:

- (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];
}

Credit goes to Christof Dorner for his simple solution (which I modified and pasted here).

物价感观 2024-09-26 12:19:51

我已经为这个问题开发了一个临时解决方法。
它使用方法调配来替换 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

I've developed a drop-in workaround for this problem.
It uses method swizzling to replace the behavior of the "imageWithContentsOfFile:" method of UIImage.
It works fine on iPhones/iPods pre/post retina.
Not sure about the iPad.

Hope this is of help.

#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
醉生梦死 2024-09-26 12:19:50

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

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

Enhancing Lisa Rossellis's answer to keep retina images at desired size (not scaling them up):

NSString *imagePath = ...Path to your image
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imagePath] scale:[UIScreen mainScreen].scale];
旧时光的容颜 2024-09-26 12:19:48

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

imageWithContentsOfFile works properly (considering @2x images with correct scale) starting iOS 4.1 and onwards.

梦醒时光 2024-09-26 12:19:46

我们刚刚在工作中遇到了这个。
这是我的解决方法,似乎有效:

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

We just ran into this here at work.
Here is my work-around that seems to hold water:

NSString *imgFile = ...path to your file;
NSData *imgData = [[NSData alloc] initWithContentsOfFile:imgFile];
UIImage *img = [[UIImage alloc] initWithData:imgData];
只是在用心讲痛 2024-09-26 12:19:43

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

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

[UIImage initWithCGImage:scale:orientation:]

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

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

Ok, actual solution found by Michael here :
http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/

He figured out that UIImage has the method "initWithCGImage" which also takes a scale factor as input (I guess the only method where you can set yourself the scale factor)

[UIImage initWithCGImage:scale:orientation:]

And this seems to work great, you can custom load your high res images and just set that the scale factor is 2.0

The problem with imageWithContentsOfFile is that since it currently does not work properly, we can't trust it even when it's fixed (because some users will still have an older iOS on their devices)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文