-
-
Save bstahlhood/3711077 to your computer and use it in GitHub Desktop.
// | |
// UIImage+Retina4.h | |
// StunOMatic | |
// | |
// Created by Benjamin Stahlhood on 9/12/12. | |
// Copyright (c) 2012 DS Media Labs. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
@interface UIImage (Retina4) | |
@end |
// | |
// UIImage+Retina4.m | |
// StunOMatic | |
// | |
// Created by Benjamin Stahlhood on 9/12/12. | |
// Copyright (c) 2012 DS Media Labs. All rights reserved. | |
// | |
#import "UIImage+Retina4.h" | |
#import <objc/objc-runtime.h> | |
static Method origImageNamedMethod = nil; | |
@implementation UIImage (Retina4) | |
+ (void)initialize { | |
origImageNamedMethod = class_getClassMethod(self, @selector(imageNamed:)); | |
method_exchangeImplementations(origImageNamedMethod, | |
class_getClassMethod(self, @selector(retina4ImageNamed:))); | |
} | |
+ (UIImage *)retina4ImageNamed:(NSString *)imageName { | |
NSLog(@"Loading image named => %@", imageName); | |
NSMutableString *imageNameMutable = [imageName mutableCopy]; | |
NSRange retinaAtSymbol = [imageName rangeOfString:@"@"]; | |
if (retinaAtSymbol.location != NSNotFound) { | |
[imageNameMutable insertString:@"-568h" atIndex:retinaAtSymbol.location]; | |
} else { | |
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height; | |
if ([UIScreen mainScreen].scale == 2.f && screenHeight == 568.0f) { | |
NSRange dot = [imageName rangeOfString:@"."]; | |
if (dot.location != NSNotFound) { | |
[imageNameMutable insertString:@"-568h@2x" atIndex:dot.location]; | |
} else { | |
[imageNameMutable appendString:@"-568h@2x"]; | |
} | |
} | |
} | |
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageNameMutable ofType:@"png"]; | |
if (imagePath) { | |
return [UIImage retina4ImageNamed:imageNameMutable]; | |
} else { | |
return [UIImage retina4ImageNamed:imageName]; | |
} | |
return nil; | |
} | |
@end |
// | |
// UIImage+Retina4.h | |
// StunOMatic | |
// | |
// Created by Benjamin Stahlhood on 9/12/12. | |
// Copyright (c) 2012 DS Media Labs. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
@interface UIImage (Retina4) | |
@end |
// | |
// UIImage+Retina4.m | |
// StunOMatic | |
// | |
// Created by Benjamin Stahlhood on 9/12/12. | |
// Copyright (c) 2012 DS Media Labs. All rights reserved. | |
// | |
#import "UIImage+Retina4.h" | |
#import <objc/runtime.h> | |
#import <objc/message.h> | |
static Method origImageNamedMethod = nil; | |
@implementation UIImage (Retina4) | |
+ (void)initialize { | |
origImageNamedMethod = class_getClassMethod(self, @selector(imageNamed:)); | |
method_exchangeImplementations(origImageNamedMethod, | |
class_getClassMethod(self, @selector(retina4ImageNamed:))); | |
} | |
+ (UIImage *)retina4ImageNamed:(NSString *)imageName { | |
NSLog(@"Loading image named => %@", imageName); | |
NSMutableString *imageNameMutable = [imageName mutableCopy]; | |
NSRange retinaAtSymbol = [imageName rangeOfString:@"@"]; | |
if (retinaAtSymbol.location != NSNotFound) { | |
[imageNameMutable insertString:@"-568h" atIndex:retinaAtSymbol.location]; | |
} else { | |
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height; | |
if ([UIScreen mainScreen].scale == 2.f && screenHeight == 568.0f) { | |
NSRange dot = [imageName rangeOfString:@"."]; | |
if (dot.location != NSNotFound) { | |
[imageNameMutable insertString:@"-568h@2x" atIndex:dot.location]; | |
} else { | |
[imageNameMutable appendString:@"-568h@2x"]; | |
} | |
} | |
} | |
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageNameMutable ofType:@"png"]; | |
if (imagePath) { | |
return [UIImage retina4ImageNamed:imageNameMutable]; | |
} else { | |
return [UIImage retina4ImageNamed:imageName]; | |
} | |
return nil; | |
} | |
@end |
By overriding the +initialize
method in the Retina4 category, you just nuked the cache flushing mechanism since it’s setup in +[UIImage initialize]
.
You should put the swizzle code in the +load
method instead of the +initialize
method. Also, this gist has UIImage+Retina4.h and UIImage+Retina4.m two times.
attribute((constructor)) is your friend :)
I prefer +load
over __attribute__((constructor))
but that’s really a matter of taste.
And what about this image name: @"MyBundle.bundle/picture.png"? In this case your code will try to insert @"-568h@2x" before the wrong dot.
This category won't work for image names which include extension.
You have to delete extension from imageNameMutable before 40'th line.
How about not even swizzling this unless you know you are running on a retina4 device?
I think it's a good idea
I rewrote the code to fix some of the commented issues. Check it out at: http://angelolloqui.com/blog/20-iPhone5-image-loading
Apple rejects apps that use method swizzling http://stackoverflow.com/questions/7720947/method-swizzling-in-ios-5
Apple doesn't reject apps that use method swizzling. The rejected app in the provided link swizzles 'dealloc' method, and that's clearly mentioned in Apple's email there.
For this case it should be completely OK. I already have several apps in the store using method swizzling.
@bstahlhood thanks for this... what would be the license for these snippets?
How is this working out for you? For me it is loading the image thinking that it has a scale of 1.0. I found that I needed to create the image, setting its scale to properly work:
if (imagePath) {
UIImage* image = [UIImage retina4ImageNamed:imageNameMutable];
if (isIPhone5) {
image = [UIImage imageWithCGImage:image.CGImage scale:2.0f orientation:UIImageOrientationUp];
}