Xcode 5 & Asset Catalog: How to reference the LaunchImage?

IosXcodeIos7Xcode5Asset Catalog

Ios Problem Overview


I am using Xcode 5's Asset Catalog, and I would like to use my LaunchImage as the background image of my home view (a pretty common practice to make the transition from 'loading' to 'loaded' look smooth).

I would like to use the same entry in the Asset Catalog to save space and not have to replicate the image in two different Image Sets.

However, calling:

UIImage *image = [UIImage imageNamed:@"LaunchImage"]; //returns nil

Ios Solutions


Solution 1 - Ios

This is the (almost) complete list of the LaunchImage (excluding the iPad images with no status bar):

Solution 2 - Ios

- (NSString *)splashImageNameForOrientation:(UIInterfaceOrientation)orientation {
    CGSize viewSize = self.view.bounds.size;
    NSString* viewOrientation = @"Portrait";
    if (UIDeviceOrientationIsLandscape(orientation)) {
        viewSize = CGSizeMake(viewSize.height, viewSize.width);
        viewOrientation = @"Landscape";
    }
    
    NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
    for (NSDictionary* dict in imagesDict) {
        CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);
        if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]])
            return dict[@"UILaunchImageName"];
    }
    return nil;
}

Solution 3 - Ios

The LaunchImages are special, and aren't actually an asset catalog on the device. If you look using iFunBox/iExplorer/etc (or on the simulator, or in the build directory) you can see the final names, and then write code to use them - eg. for an iOS7-only iPhone-only project, this will set the right launch image:

NSString *launchImage;
if  ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) &&
     ([UIScreen mainScreen].bounds.size.height > 480.0f)) {
    launchImage = @"LaunchImage-700-568h";
} else {
    launchImage = @"LaunchImage-700";
}

[self.launchImageView setImage:[UIImage imageNamed:launchImage]];

I put this into viewDidLoad.

This isn't really ideal, it would be great if Apple would give us a nice API to do this.

Solution 4 - Ios

My app currently only supports iOS 7 and later.

This is how I reference the launch image from the asset catalog:

NSDictionary *dict = @{@"320x480" : @"LaunchImage-700",
                       @"320x568" : @"LaunchImage-700-568h",
                       @"375x667" : @"LaunchImage-800-667h",
                       @"414x736" : @"LaunchImage-800-Portrait-736h"};
NSString *key = [NSString stringWithFormat:@"%dx%d",
    (int)[UIScreen mainScreen].bounds.size.width,
    (int)[UIScreen mainScreen].bounds.size.height];
UIImage *launchImage = [UIImage imageNamed:dict[key]];

You can add more key value pairs if you want to support older iOS versions.

Solution 5 - Ios

Here a category on UIImage based on the solution provided by Cherpak Evgeny above.

UIImage+SplashImage.h:

#import <UIKit/UIKit.h>

/**
 * Category on `UIImage` to access the splash image.
 **/
@interface UIImage (SplashImage)

/**
 * Return the name of the splash image for a given orientation.
 * @param orientation The interface orientation.
 * @return The name of the splash image.
 **/
+ (NSString *)si_splashImageNameForOrientation:(UIInterfaceOrientation)orientation;

/**
 * Returns the splash image for a given orientation.
 * @param orientation The interface orientation.
 * @return The splash image.
 **/
+ (UIImage*)si_splashImageForOrientation:(UIInterfaceOrientation)orientation;

@end

UIImage+SplashImage.m:

#import "UIImage+SplashImage.h"

@implementation UIImage (SplashImage)

+ (NSString *)si_splashImageNameForOrientation:(UIInterfaceOrientation)orientation
{
    CGSize viewSize = [UIScreen mainScreen].bounds.size;
    
    NSString *viewOrientation = @"Portrait";
    
    if (UIDeviceOrientationIsLandscape(orientation))
    {
        viewSize = CGSizeMake(viewSize.height, viewSize.width);
        viewOrientation = @"Landscape";
    }
    
    NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
    
    for (NSDictionary *dict in imagesDict)
    {
        CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);
        if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]])
            return dict[@"UILaunchImageName"];
    }
    return nil;
}

+ (UIImage*)si_splashImageForOrientation:(UIInterfaceOrientation)orientation
{
    NSString *imageName = [self si_splashImageNameForOrientation:orientation];
    UIImage *image = [UIImage imageNamed:imageName];
    return image;
}

@end

Solution 6 - Ios

@codeman's answer updated for Swift 1.2:

func splashImageForOrientation(orientation: UIInterfaceOrientation, size: CGSize) -> String? {
    var viewSize        = size
    var viewOrientation = "Portrait"
    
    if UIInterfaceOrientationIsLandscape(orientation) {
        viewSize        = CGSizeMake(size.height, size.width)
        viewOrientation = "Landscape"
    }
    
    if let imagesDict = NSBundle.mainBundle().infoDictionary as? [String: AnyObject] {
        if let imagesArray = imagesDict["UILaunchImages"] as? [[String: String]] {
            for dict in imagesArray {
                if let sizeString = dict["UILaunchImageSize"], let imageOrientation = dict["UILaunchImageOrientation"] {
                    let imageSize = CGSizeFromString(sizeString)
                    if CGSizeEqualToSize(imageSize, viewSize) && viewOrientation == imageOrientation {
                        if let imageName = dict["UILaunchImageName"] {
                            return imageName
                        }
                    }
                }
            }
        }
    }
    
    return nil
    
}

To call it, and to support rotation for iOS 8:

override func viewWillAppear(animated: Bool) {
    if let img = splashImageForOrientation(UIApplication.sharedApplication().statusBarOrientation, size: self.view.bounds.size) {
        backgroundImage.image = UIImage(named: img)
    }
}

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    let orientation = size.height > size.width ? UIInterfaceOrientation.Portrait : UIInterfaceOrientation.LandscapeLeft
    
    if let img = splashImageForOrientation(orientation, size: size) {
        backgroundImage.image = UIImage(named: img)
    }
    
}

Just what I needed, thanks!

Solution 7 - Ios

I just wrote a general method to get the splash image name for iPhone and iPad (Landscape, Portrait), It worked for me, Hope It helps you as well. I wrote this with help of other SO answers, thanks @Pichirichi for whole list.

+(NSString*)getLaunchImageName
{

 NSArray* images= @[@"LaunchImage.png", @"[email protected]",@"[email protected]",@"[email protected]",@"[email protected]",@"LaunchImage-700-Portrait@2x~ipad.png",@"LaunchImage-Portrait@2x~ipad.png",@"LaunchImage-700-Portrait~ipad.png",@"LaunchImage-Portrait~ipad.png",@"LaunchImage-Landscape@2x~ipad.png",@"LaunchImage-700-Landscape@2x~ipad.png",@"LaunchImage-Landscape~ipad.png",@"LaunchImage-700-Landscape~ipad.png"];

UIImage *splashImage;

if ([self isDeviceiPhone])
{
    if ([self isDeviceiPhone4] && [self isDeviceRetina])
    {
        splashImage = [UIImage imageNamed:images[1]];
        if (splashImage.size.width!=0)
            return images[1];
        else
            return images[2];
    }
    else if ([self isDeviceiPhone5])
    {
        splashImage = [UIImage imageNamed:images[1]];
        if (splashImage.size.width!=0)
            return images[3];
        else
            return images[4];
    }
    else
        return images[0]; //Non-retina iPhone
}
else if ([[UIDevice currentDevice] orientation]==UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown)//iPad Portrait
{
    if ([self isDeviceRetina])
    {
        splashImage = [UIImage imageNamed:images[5]];
        if (splashImage.size.width!=0)
            return images[5];
        else
            return images[6];
    }
    else
    {
        splashImage = [UIImage imageNamed:images[7]];
        if (splashImage.size.width!=0)
            return images[7];
        else
            return images[8];
    }
    
}
else
{
    if ([self isDeviceRetina])
    {
        splashImage = [UIImage imageNamed:images[9]];
        if (splashImage.size.width!=0)
            return images[9];
        else
            return images[10];
    }
    else
    {
        splashImage = [UIImage imageNamed:images[11]];
        if (splashImage.size.width!=0)
            return images[11];
        else
            return images[12];
    }
 }
}

Other utility methods are

+(BOOL)isDeviceiPhone
{
 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
 {
     return TRUE;
 }

 return FALSE;
}

+(BOOL)isDeviceiPhone4
{
 if ([[UIScreen mainScreen] bounds].size.height==480)
    return TRUE;

 return FALSE;
}


+(BOOL)isDeviceRetina
{
 if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
    ([UIScreen mainScreen].scale == 2.0))        // Retina display
 {
    return TRUE;
 } 
 else                                          // non-Retina display
 {
     return FALSE;
 }
}


+(BOOL)isDeviceiPhone5
{
 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && [[UIScreen mainScreen] bounds].size.height>480)
 {
    return TRUE;
 }
 return FALSE;
}

Solution 8 - Ios

Swift version of Cherpak Evgeny's answer:

    func splashImageForOrientation(orientation: UIInterfaceOrientation) -> String {
        var viewSize = self.view.bounds.size
        var viewOrientation = "Portrait"
        if UIInterfaceOrientationIsLandscape(orientation) {
           viewSize = CGSizeMake(viewSize.height, viewSize.width)
           viewOrientation = "Landscape"
        }
        let imagesDict = NSBundle.mainBundle().infoDictionary as Dictionary<NSObject,AnyObject>!
        let imagesArray = imagesDict["UILaunchImages"] as NSArray
        for dict in imagesArray {
            let dictNSDict = dict as NSDictionary
            let imageSize = CGSizeFromString(dictNSDict["UILaunchImageSize"] as String)
            if CGSizeEqualToSize(imageSize, viewSize) && viewOrientation == (dictNSDict["UILaunchImageOrientation"] as String) {
                return dictNSDict["UILaunchImageName"] as String
            }
        }
        return ""
    }

Solution 9 - Ios

Following @Pichirich's answer, I referenced my launchimage in InterfaceBuilder as:

"LaunchImage.png"

...and with Xcode 5.0.2, it's automatically plucking the appropriate image straight out of the Asset Catalog.

This is what I'd expect - except for Apple's viciously nasty move of silently renaming "Default.png" to "LaunchImage.png" :)

Solution 10 - Ios

In the documentation there is clearly stated:

"Each set in an asset catalog has a name. You can use that name to programmatically load any individual image contained in the set. To load an image, call the UIImage:ImageNamed: method, passing the name of the set that contains the image."

Using Pichirichi's list helps to solve this inconsistency.

Solution 11 - Ios

One can easily access Launch image by one line of code.

 UIImage *myAppsLaunchImage = [UIImage launchImage];

Please follow steps given below to achieve functionality depicted above.

Step 1. Extend UIImage class by creating a category & add following method to it.

+ (UIImage *)launchImage {
    NSDictionary *dOfLaunchImage = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @"[email protected]",@"568,320,2,8,p", // ios 8 - iphone 5 - portrait
                                    @"[email protected]",@"568,320,2,8,l", // ios 8 - iphone 5 - landscape
                                    @"[email protected]",@"568,320,2,7,p", // ios 7 - iphone 5 - portrait
                                    @"[email protected]",@"568,320,2,7,l", // ios 7 - iphone 5 - landscape
                                    @"LaunchImage-700-Landscape@2x~ipad.png",@"1024,768,2,7,l", // ios 7 - ipad retina - landscape
                                    @"LaunchImage-700-Landscape~ipad.png",@"1024,768,1,7,l", // ios 7 - ipad regular - landscape
                                    @"LaunchImage-700-Portrait@2x~ipad.png",@"1024,768,2,7,p", // ios 7 - ipad retina - portrait
                                    @"LaunchImage-700-Portrait~ipad.png",@"1024,768,1,7,p", // ios 7 - ipad regular - portrait
                                    @"[email protected]",@"480,320,2,7,p", // ios 7 - iphone 4/4s retina - portrait
                                    @"[email protected]",@"480,320,2,7,l", // ios 7 - iphone 4/4s retina - landscape
                                    @"LaunchImage-Landscape@2x~ipad.png",@"1024,768,2,8,l", // ios 8 - ipad retina - landscape
                                    @"LaunchImage-Landscape~ipad.png",@"1024,768,1,8,l", // ios 8 - ipad regular - landscape
                                    @"LaunchImage-Portrait@2x~ipad.png",@"1024,768,2,8,p", // ios 8 - ipad retina - portrait
                                    @"LaunchImage-Portrait~ipad.png",@"1024,768,1,8,l", // ios 8 - ipad regular - portrait
                                    @"LaunchImage.png",@"480,320,1,7,p", // ios 6 - iphone 3g/3gs - portrait
                                    @"LaunchImage.png",@"480,320,1,7,l", // ios 6 - iphone 3g/3gs - landscape
                                    @"[email protected]",@"480,320,2,8,p", // ios 6,7,8 - iphone 4/4s - portrait
                                    @"[email protected]",@"480,320,2,8,l", // ios 6,7,8 - iphone 4/4s - landscape
                                    @"[email protected]",@"667,375,2,8,p", // ios 8 - iphone 6 - portrait
                                    @"[email protected]",@"667,375,2,8,l", // ios 8 - iphone 6 - landscape
                                    @"[email protected]",@"736,414,3,8,p", // ios 8 - iphone 6 plus - portrait
                                    @"[email protected]",@"736,414,3,8,l", // ios 8 - iphone 6 plus - landscape
                                    nil];
    NSInteger width = ([UIScreen mainScreen].bounds.size.width>[UIScreen mainScreen].bounds.size.height)?[UIScreen mainScreen].bounds.size.width:[UIScreen mainScreen].bounds.size.height;
    NSInteger height = ([UIScreen mainScreen].bounds.size.width>[UIScreen mainScreen].bounds.size.height)?[UIScreen mainScreen].bounds.size.height:[UIScreen mainScreen].bounds.size.width;
    NSInteger os = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."] objectAtIndex:0] integerValue];
    NSString *strOrientation = UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation])?@"l":@"p";
    NSString *strImageName = [NSString stringWithFormat:@"%li,%li,%li,%li,%@",width,height,(NSInteger)[UIScreen mainScreen].scale,os,strOrientation];
    UIImage *imageToReturn = [UIImage imageNamed:[dOfLaunchImage valueForKey:strImageName]];
    if([strOrientation isEqualToString:@"l"] && [strImageName rangeOfString:@"Landscape"].length==0) {
        imageToReturn = [UIImage rotate:imageToReturn orientation:UIImageOrientationRight];
    }
    return imageToReturn;
}

Step 2. Above method should be working by adding following code also into same category of UIImage

static inline double radians (double degrees) {return degrees * M_PI/180;}

+ (UIImage *)rotate:(UIImage*)src orientation:(UIImageOrientation) orientation {
    UIGraphicsBeginImageContext(src.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    if (orientation == UIImageOrientationRight) {
        CGContextRotateCTM (context, radians(90));
    } else if (orientation == UIImageOrientationLeft) {
        CGContextRotateCTM (context, radians(-90));
    } else if (orientation == UIImageOrientationDown) {
        // NOTHING
    } else if (orientation == UIImageOrientationUp) {
        CGContextRotateCTM (context, radians(90));
    }
    [src drawAtPoint:CGPointMake(0, 0)];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

Solution 12 - Ios

I realize that this is not necessarily the best solution for everyone but the easiest (and least error-prone, IMHO) way to do this is by making a separate entry in your Images.xcassets catalog. I called it SplashImage.

When you go to add a new entry, make sure not to select "New Launch Image" as an option. Instead, select the generic "New Image Set". Next, open up the inspector and select the relevant options. If you're building for only retina devices, as I was, you can select the following:

image inspector

This will leave you with four entries (iPhone 4S, iPhone 5(s,c), iPhone 6, and iPhone 6 Plus).

images

The files corresponding the the images are as follows:

| Resolution (Xcode entry) | Launch Image name   |   Device         |
|--------------------------|---------------------|------------------|
| 1x                       | Default-750.png     | iPhone 6         |
| 2x                       | Default@2x.png      | iPhone 4S        |
| Retina 4 2x              | Default-568h@2x.png | iPhone 5, 5s, 5c |
| 3x                       | Default-1242.png    | iPhone 6 Plus    |

Of course, after you've done this you can simply use [UIImage imageNamed:@"SplashImage"]

Solution 13 - Ios

With help of Pichirichi's answer I've implemented the following category (iOS 7+) : UIImage+AssetLaunchImage

It's actually little more than generating name on the fly, but probably will be helpful.

Solution 14 - Ios

Updated to latest Swift syntax (Swift 5)

   func splashImageForOrientation(orientation: UIInterfaceOrientation) -> String? {

    var viewSize = screenSize
    var viewOrientation = "Portrait"
    if orientation.isLandscape {
        viewSize = CGSize(width: viewSize.height, height: viewSize.width)
        viewOrientation = "Landscape"
    }
    if let infoDict = Bundle.main.infoDictionary, let launchImagesArray = infoDict["UILaunchImages"] as? [Any] {
        for launchImage in launchImagesArray {
            if let launchImage = launchImage as? [String: Any], let nameString = launchImage["UILaunchImageName"] as? String, let sizeString = launchImage["UILaunchImageSize"] as? String, let orientationString = launchImage["UILaunchImageOrientation"] as? String {
                let imageSize = NSCoder.cgSize(for: sizeString)
                if imageSize.equalTo(viewSize) && viewOrientation == orientationString {
                    return nameString
                }
            }
        }
    }
    return nil
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontoblerpwnView Question on Stackoverflow
Solution 1 - IosPichirichiView Answer on Stackoverflow
Solution 2 - IosCherpak EvgenyView Answer on Stackoverflow
Solution 3 - IosJosephHView Answer on Stackoverflow
Solution 4 - IosnonameliveView Answer on Stackoverflow
Solution 5 - IosvilanoviView Answer on Stackoverflow
Solution 6 - IosAnthony ScottView Answer on Stackoverflow
Solution 7 - IoszaheerView Answer on Stackoverflow
Solution 8 - IoscodemanView Answer on Stackoverflow
Solution 9 - IosAdamView Answer on Stackoverflow
Solution 10 - IosVladimír SlavíkView Answer on Stackoverflow
Solution 11 - IosSagar KothariView Answer on Stackoverflow
Solution 12 - IosMichael DiStefanoView Answer on Stackoverflow
Solution 13 - IosTimur KuchkarovView Answer on Stackoverflow
Solution 14 - IosPau BalladaView Answer on Stackoverflow