Как заставить работать [UIImage imageWithContentsOfFile:] и изображения с высоким разрешением
Как многие люди жалуются, кажется, что в Apple SDK для Retina Display есть ошибка, и imageWithContentsOfFile фактически не загружает 2x изображения автоматически.
Я наткнулся на хороший пост, как создать функцию, которая распознает масштабный коэффициент UIScreen и правильно загружает изображения с низким или высоким разрешением ( http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/), но решение загружает 2- кратное изображение и все еще имеет масштабный коэффициент изображения, равный 1,0, и в результате 2-кратное изображение масштабируется в 2 раза (то есть в 4 раза больше, чем должно быть)
imageNamed, кажется, точно загружает изображения с низким и высоким разрешением, но это не вариант для меня.
У кого-нибудь есть решение для загрузки изображений с низким / высоким разрешением, не использующее автоматическую загрузку imageNamed или imageWithContentsOfFile? (Или, в конце концов, решение, как заставить imageWithContentsOfFile работать правильно)
6 ответов
Хорошо, фактическое решение, найденное Майклом здесь: http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/
Он выяснил, что в UIImage есть метод "initWithCGImage", который также принимает масштабный коэффициент в качестве входных данных (я думаю, единственный метод, где вы можете установить масштабный коэффициент самостоятельно)
[UIImage initWithCGImage:scale:orientation:]
И это, кажется, прекрасно работает, вы можете загрузить свои изображения в высоком разрешении и установить масштабный коэффициент 2,0.
Проблема с imageWithContentsOfFile состоит в том, что, поскольку он в настоящее время не работает должным образом, мы не можем доверять ему, даже когда он исправлен (потому что у некоторых пользователей все еще будет более старая iOS на их устройствах)
Мы просто столкнулись с этим здесь на работе. Вот мой обходной путь, который, кажется, держит воду:
NSString *imgFile = ...path to your file;
NSData *imgData = [[NSData alloc] initWithContentsOfFile:imgFile];
UIImage *img = [[UIImage alloc] initWithData:imgData];
imageWithContentsOfFile
работает должным образом (учитывая @2x изображения с правильным масштабом) начиная с iOS 4.1 и выше.
Усиление ответа Лизы Росселлис, чтобы сохранить изображения на сетчатке в желаемом размере (не масштабируя их):
NSString *imagePath = ...Path to your image
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imagePath] scale:[UIScreen mainScreen].scale];
Я разработал обходной путь для этой проблемы. Он использует метод swizzling для замены поведения метода "imageWithContentsOfFile:" UIImage. Он отлично работает на 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
[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];
}
Благодарим Кристофа Дорнера за его простое решение (которое я здесь изменил и вставил).