2010-09-17 12 views
11

Como mucha gente se queja, parece que en el Apple SDK para Retina Display hay un error y imageWithContentsOfFile en realidad no carga automáticamente las imágenes 2x.Cómo obtener [UIImage imageWithContentsOfFile:] e imágenes de alta resolución que funcionan

Me he topado con una buena publicación sobre cómo hacer una función que detecta el factor de escala UIScreen y carga correctamente imágenes de baja o alta resolución (http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/), pero la solución carga una imagen 2x y todavía tiene el factor de escala de la imagen se establece en 1.0 y esto da como resultado 2x imágenes escaladas 2 veces (4 veces más grande que lo que parece)

imageNamed parece cargar con precisión imágenes de baja y alta resolución, pero no es una opción para mí.

¿Alguien tiene una solución para cargar imágenes de baja/alta resolución sin usar la carga automática de imageNamed o imageWithContentsOfFile? (O eventualmente solución cómo hacer el trabajo imageWithContentsOfFile correcta)

Respuesta

13

solución Ok, real encontrado por Michael aquí: http://atastypixel.com/blog/uiimage-resolution-independence-and-the-iphone-4s-retina-display/

Él cuenta de que UIImage tiene el método "initWithCGImage" que también tiene un factor de escala como entrada (Supongo que el único método en el que usted puede configurar el factor de escala)

[UIImage initWithCGImage:scale:orientation:] 

y esto parece que funciona muy bien, puede cargar su alta res imágenes y acaba de establecer que el factor de escala es de 2,0

El problema con imageWithContentsOfFile es que, dado que actualmente no funciona correctamente, no podemos confiar incluso cuando está solucionado (porque algunos usuarios todavía tendrán un iOS anterior en sus dispositivos)

+0

si apoya a iOS 4.1 o más tarde, vea la respuesta por 'bioffe'. Mucho más simple –

5

He desarrollado una caída -en la solución para este problema. Utiliza el método Swizzling para reemplazar el comportamiento del método "imageWithContentsOfFile:" de UIImage. Funciona bien en iPhones/iPods pre/post retina. No estoy seguro acerca del iPad.

Espero que esto sea de ayuda.

#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 
+0

Hola Marco, como se describe en la pregunta: los nombres de los archivos no son un problema en absoluto, sino el factor de escala de la imagen en sí. –

+0

Hola Ican, mi código establece correctamente el factor de escala (probado en el simulador con OS 4.0.1 y 4.1). El punto es que mientras imageWithContentsOfFile: no encuentra automáticamente la variante "@ 2x", como dices, una vez que le indicas que obtenga el archivo "@ 2x" directamente (como en mi método swizzled), entonces establece correctamente el factor de escala. Simplemente coloque mis dos categorías en algún lugar de su código donde se compilan e intente usar imageWithContentsOfFile: como lo haría si no estuviera roto –

9

imageWithContentsOfFile funciona correctamente (teniendo en cuenta @ 2x imágenes con escala correcta) a partir de iOS 4.1 en adelante.

+1

A pesar de lo que dice la documentación, 'imageWithContentsOfFile:' no funciona en rutas absolutas. Probado en iOS 5.1 – bentford

+0

@bentford funciona en iOS 5.1 para mí – bioffe

+1

No funciona para la ruta absoluta de las imágenes en la carpeta Caches. Investigaremos más a fondo, tal vez fue una mala prueba. – bentford

3

[UIImage imageWithContentsOfFile:] no carga @ 2x gráficos si especifica una ruta absoluta.

Aquí es una solución:

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

crédito va a Christof Dorner por su solución simple (que he modificado y pegar aquí).

11

Acabamos de encontrarnos con esto aquí en el trabajo. Aquí está mi solución alternativa que parece contener el agua:

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

Mejora de la respuesta de Lisa Rossellis de mantener las imágenes de la retina al tamaño deseado (no extender su aplicación):

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