Skip to content

Instantly share code, notes, and snippets.

@bstahlhood
Created September 13, 2012 00:51
Show Gist options
  • Star 46 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save bstahlhood/3711077 to your computer and use it in GitHub Desktop.
Save bstahlhood/3711077 to your computer and use it in GitHub Desktop.
Swizzled UIImage imageNamed for iPhone 5
//
// 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
@waynehartman
Copy link

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

    return image;
} else {
    return [UIImage retina4ImageNamed:imageName];
}

@0xced
Copy link

0xced commented Sep 14, 2012

By overriding the +initialize method in the Retina4 category, you just nuked the cache flushing mechanism since it’s setup in +[UIImage initialize].

@0xced
Copy link

0xced commented Sep 14, 2012

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.

@steipete
Copy link

attribute((constructor)) is your friend :)

@0xced
Copy link

0xced commented Sep 14, 2012

I prefer +load over __attribute__((constructor)) but that’s really a matter of taste.

@artemstepanenko
Copy link

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.

@artemstepanenko
Copy link

This category won't work for image names which include extension.
You have to delete extension from imageNameMutable before 40'th line.

@rufferto
Copy link

How about not even swizzling this unless you know you are running on a retina4 device?

@artemstepanenko
Copy link

I think it's a good idea

@angelolloqui
Copy link

I rewrote the code to fix some of the commented issues. Check it out at: http://angelolloqui.com/blog/20-iPhone5-image-loading

@plenilune
Copy link

Apple rejects apps that use method swizzling http://stackoverflow.com/questions/7720947/method-swizzling-in-ios-5

@furkanmustafa
Copy link

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.

@pombredanne
Copy link

@bstahlhood thanks for this... what would be the license for these snippets?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment