Skip to content

Instantly share code, notes, and snippets.

@kevindelord
Last active June 6, 2016 15:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kevindelord/fe2e691d06ab745fbb00 to your computer and use it in GitHub Desktop.
Save kevindelord/fe2e691d06ab745fbb00 to your computer and use it in GitHub Desktop.
UIImage extension to automatically generate a valid UIImage on iPhone using an asset corresponding to the actual device screen size. If needed, this code will add the following image suffix: @2x, -568h@2x, -667h@2x, @3x.
//
// UIImage+Autoresize.h
// UIImage+Autoresize
//
// Created by kevin delord on 24/04/14.
// Copyright (c) 2014 Kevin Delord. All rights reserved.
//
// Full library and documentation here: https://github.com/kevindelord/UIImage-Autoresize
//
#ifndef UIImage_Autoresize_h__
#define UIImage_Autoresize_h__
#import <UIKit/UIKit.h>
/**
* A categorized class of UIImage replacing the default `UIImage::imageNamed:` method with a custom one.
* Mainly used for fullscreen background images, this class adds a naming convetion to deal image assets
* with different sizes, orientations and scales.
*
* The suffixes are describe in the `README.md`.
*
*/
@interface UIImage (Autoresize)
#pragma mark - UIImage Initializer
/**
* Method to override the `UIImage::imageNamed:` method with a custom one.
* The new method and its implementation will be executed instead of the default UIImage::imageNamed:
* The user don't need to do anything.
*/
+ (void)load;
/**
* Returns a new UIImage object created from a filename.
*
* @discussion This method will automatically add the needed image suffix for the current device.
* It also deals with the device interface orientation and different iOS versions.
* Important: the given filename should NOT contain any size-extension, only a name and its file type.
*
* @param imageName The NSString object representing the filename of the image.
* @return An UIImage created from a given string name.
*/
+ (UIImage *)dynamicImageNamed:(NSString *)imageName;
/**
* Returns a new UIImage object created from a filename and a required transition Size.
*
* @discussion This function should be called from a view controller when the device changes its orientation.
* The function `viewWillTransitionToSize:withTransitionCoordinator:` is only available from iOS 8.
*
* @param imageName The NSString object representing the filename of the image.
* @param size The new size for the container’s view.
* @return An UIImage created from a generated string name.
*/
+ (UIImage *)imageNamed:(NSString *)imageName withTransitionSize:(CGSize)size;
@end
#endif
//
// UIImage+Autoresize.m
// UIImage+Autoresize
//
// Created by kevin delord on 24/04/14.
// Copyright (c) 2014 Kevin Delord. All rights reserved.
//
// Full library and documentation here: https://github.com/kevindelord/UIImage-Autoresize
//
#import "UIImage+Autoresize.h"
#import <objc/runtime.h>
#define __K_DEBUG_LOG_UIIMAGE_AUTORESIZE_ENABLED__ false
@implementation UIImage (Autoresize)
#pragma mark - UIImage Initializer
/**
* This function actually do the magic trick. When called for the first time it will swizz (replace)
* the normal `imageNamed:` method with a custom one implemented in this library.
*/
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method origImageNamedMethod = class_getClassMethod(self.class, @selector(imageNamed:));
method_exchangeImplementations(origImageNamedMethod, class_getClassMethod(self.class, @selector(dynamicImageNamed:)));
});
}
/**
* Returns a valid suffix string to use with a portrait/vertical image file.
* It takes as parameters the desired height and witdh of the screen.
*/
+ (NSString *)verticalExtensionForHeight:(CGFloat)h width:(CGFloat)w {
//
// Get the current device scale
CGFloat scale = [UIScreen mainScreen].scale;
if (__K_DEBUG_LOG_UIIMAGE_AUTORESIZE_ENABLED__) {
NSLog(@"--------------- VERTICAL ----------------------");
NSLog(@"h: %f", h);
NSLog(@"w: %f", w);
NSLog(@"scale: %f", scale);
NSLog(@"-------------------------------------------------");
}
// Generate the current valid file extension depending on the current device screen size.
NSString *extension = @""; // iPhone 3GS and earlier
if (scale == 3.f) {
extension = @"@3x"; // iPhone 6 Plus
} else if (scale == 2.f && h == 568.0f && w == 320.0f) {
extension = @"-568h@2x"; // iPhone 5, 5S, 5C
} else if (scale == 2.f && h == 667.0f && w == 375.0f) {
extension = @"-667h@2x"; // iPhone 6
} else if (scale == 2.f && h == 480.0f && w == 320.0f) {
extension = @"@2x"; // iPhone 4, 4S
} else if (scale == 1.f && h == 1024.0f && w == 768.0f) {
extension = @"-512h"; // iPad Mini, iPad 2, iPad 1
} else if (scale == 2.f && h == 1024.0f && w == 768.0f) {
extension = @"-1024h@2x"; // iPad Mini 3, iPad Mini 2, iPad Air, iPad Air 2
}
return extension;
}
/**
* Returns a valid suffix string to use with a landscape/horizontal image file.
* It takes as parameters the desired height and witdh of the screen.
*/
+ (NSString *)horizontalExtensionForHeight:(CGFloat)h width:(CGFloat)w {
//
// Get the current device scale
CGFloat scale = [UIScreen mainScreen].scale;
if (__K_DEBUG_LOG_UIIMAGE_AUTORESIZE_ENABLED__) {
NSLog(@"--------------- HORIZONTAL --------------------");
NSLog(@"h: %f", h);
NSLog(@"w: %f", w);
NSLog(@"scale: %f", scale);
NSLog(@"-------------------------------------------------");
}
// Generate the current valid file extension depending on the current device screen size.
NSString *extension = @"-l"; // iPhone 3GS and earlier
if (scale == 3.f) {
extension = @"-l@3x"; // iPhone 6 Plus
} else if (scale == 2.f && w == 568.0f && h == 320.0f) {
extension = @"-320h-l@2x"; // iPhone 5, 5S, 5C
} else if (scale == 2.f && w == 667.0f && h == 375.0f) {
extension = @"-375h-l@2x"; // iPhone 6
} else if (scale == 2.f && w == 480.0f && h == 320.0f) {
extension = @"-l@2x"; // iPhone 4, 4S
} else if (scale == 1.f && w == 1024.0f && h == 768.0f) {
extension = @"-384h-l"; // iPad Mini, iPad 2, iPad 1
} else if (scale == 2.f && w == 1024.0f && h == 768.0f) {
extension = @"-768h-l@2x"; // iPad Mini 3, iPad Mini 2, iPad Air, iPad Air 2
}
return extension;
}
/**
* This function calculates the required CGSize with which the UIImage should depend from.
* This size is then use to get the correct suffix of the image filename.
*
* Returns an UIImage object.
*/
+ (UIImage *)dynamicImageNamed:(NSString *)imageName {
//
// Only change the name if no '@2x' or '@3x' are specified
if ([imageName rangeOfString:@"@"].location == NSNotFound) {
UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
CGSize size = [UIScreen mainScreen].bounds.size;
//
// Before iOS 8.0 the current mainScreen bounds were giving a different result than with newest iOS version.
// The size needs to be reversed for landscape mode.
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending) {
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
CGFloat temp = size.width;
size.width = size.height;
size.height = temp;
}
}
return [self imageNamed:imageName withTransitionSize:size];
}
// Otherwise returns an UIImage with the original filename.
return [UIImage dynamicImageNamed:imageName];
}
/**
* When given a valid name and a transition size as parameters, this function will generate a new
* filename with a required suffix.
* This filename is used to create and return a new UIImage object.
*/
+ (UIImage *)imageNamed:(NSString *)imageName withTransitionSize:(CGSize)size {
//
// Only change the name if no '@2x' or '@3x' are specified
if ([imageName rangeOfString:@"@"].location == NSNotFound) {
NSString *extension = @"";
if (size.height >= size.width) {
extension = [self verticalExtensionForHeight:size.height width:size.width];
} else {
extension = [self horizontalExtensionForHeight:size.height width:size.width];
}
// Add the extension to the image name
NSRange dot = [imageName rangeOfString:@"."];
NSMutableString *imageNameMutable = [imageName mutableCopy];
if (dot.location != NSNotFound) {
[imageNameMutable insertString:extension atIndex:dot.location];
} else {
[imageNameMutable appendString:extension];
}
// If exist returns the corresponding UIImage
if ([[NSBundle mainBundle] pathForResource:imageNameMutable ofType:@""]) {
return [UIImage dynamicImageNamed:imageNameMutable];
}
}
// Otherwise returns an UIImage with the original filename.
return [UIImage dynamicImageNamed:imageName];
}
@end
@idoan
Copy link

idoan commented Sep 2, 2015

Great, thabks. I have spent a great deal of time to adjust the background image accordingly. This saves lots of time.

@JulesMoorhouse
Copy link

If would be awesome if with support x3 and had some fallback to make it future proof.

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