Xcode 5 & Каталог активов: как ссылаться на LaunchImage?
Я использую каталог активов Xcode 5, и я хотел бы использовать мой LaunchImage
как фоновое изображение моего домашнего представления (довольно распространенная практика, чтобы переход от "загрузки" к "загруженному" выглядел плавно).
Я хотел бы использовать одну и ту же запись в каталоге активов для экономии места и не нужно копировать изображение в двух разных наборах изображений.
Тем не менее, позвонив:
UIImage *image = [UIImage imageNamed:@"LaunchImage"]; //returns nil
13 ответов
Вот (почти) полный список LaunchImage (исключая изображения iPad без строки состояния):
- LaunchImage-568h@2x.png
- LaunchImage-700-568h@2x.png
- LaunchImage-700-Landscape@2x~ipad.png
- LaunchImage-700-Пейзаж ~ ipad.png
- LaunchImage-700-Portrait@2x~ipad.png
- LaunchImage-700-портрет ~ ipad.png
- LaunchImage-700@2x.png
- LaunchImage-Landscape@2x~ipad.png
- LaunchImage-Пейзаж ~ ipad.png
- LaunchImage-Portrait@2x~ipad.png
- LaunchImage-портрет ~ ipad.png
- LaunchImage.png
- LaunchImage@2x.png
- LaunchImage-800-667h@2x.png (iPhone 6)
- LaunchImage-800-Portrait-736h@3x.png (iPhone 6 Plus Portrait)
- LaunchImage-800-Landscape-736h@3x.png (iPhone 6 Plus альбомная)
- LaunchImage-1100-Portrait-2436h@3x.png (iPhone X Portrait)
- LaunchImage-1100-Landscape-2436h@3x.png (iPhone X Пейзаж)
- (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;
}
LaunchImages являются особыми и не являются каталогом ресурсов на устройстве. Если вы посмотрите с помощью iFunBox/iExplorer/etc (или на симуляторе, или в каталоге сборки), вы можете увидеть окончательные имена, а затем написать код для их использования - например. для проекта iPhone7 только для iPhone, это установит правильное изображение запуска:
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]];
Я положил это в viewDidLoad.
Это не совсем идеально, было бы здорово, если бы Apple предоставила нам хороший API для этого.
В настоящее время мое приложение поддерживает только iOS 7 и более поздние версии.
Вот как я ссылаюсь на стартовый образ из каталога ресурсов:
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]];
Вы можете добавить больше пар ключ-значение, если хотите поддерживать более старые версии iOS.
Вот категория на UIImage, основанная на решении, предоставленном Черпаком Евгением выше.
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
Обновлен ответ @codeman для 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
}
Чтобы позвонить и поддержать ротацию для 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)
}
}
Только то, что мне было нужно, спасибо!
Я только что написал общий метод получения названия заставки для iPhone и iPad (альбомная, портретная), у меня это сработало, надеюсь, вам это тоже поможет. Я написал это с помощью других ответов SO, спасибо @Pichirichi за весь список.
+(NSString*)getLaunchImageName
{
NSArray* images= @[@"LaunchImage.png", @"LaunchImage@2x.png",@"LaunchImage-700@2x.png",@"LaunchImage-568h@2x.png",@"LaunchImage-700-568h@2x.png",@"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];
}
}
}
Другие полезные методы
+(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;
}
Свифт версия ответа Черпак Евгений:
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 ""
}
Следуя ответу @Pichirich, я сослался на свой образ запуска в InterfaceBuilder как:
"LaunchImage.png"
... а с Xcode 5.0.2 он автоматически вынимает соответствующее изображение прямо из каталога активов.
Это то, что я ожидал - за исключением злобного мерзкого шага Apple по тихому переименованию "Default.png" в "LaunchImage.png":)
В документации четко указано:
"У каждого набора в каталоге ресурсов есть имя. Вы можете использовать это имя для программной загрузки любого отдельного изображения, содержащегося в наборе. Чтобы загрузить изображение, вызовите метод UIImage: ImageNamed:, передав имя набора, содержащего изображение. ".
Использование списка Пичиричи помогает решить эту проблему.
Можно легко получить доступ к изображению запуска из одной строки кода.
UIImage *myAppsLaunchImage = [UIImage launchImage];
Пожалуйста, следуйте инструкциям ниже для достижения функциональности, изображенной выше.
Шаг 1. Расширить UIImage
Класс, создав категорию и добавив к ней следующий метод.
+ (UIImage *)launchImage {
NSDictionary *dOfLaunchImage = [NSDictionary dictionaryWithObjectsAndKeys:
@"LaunchImage-568h@2x.png",@"568,320,2,8,p", // ios 8 - iphone 5 - portrait
@"LaunchImage-568h@2x.png",@"568,320,2,8,l", // ios 8 - iphone 5 - landscape
@"LaunchImage-700-568h@2x.png",@"568,320,2,7,p", // ios 7 - iphone 5 - portrait
@"LaunchImage-700-568h@2x.png",@"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
@"LaunchImage-700@2x.png",@"480,320,2,7,p", // ios 7 - iphone 4/4s retina - portrait
@"LaunchImage-700@2x.png",@"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
@"LaunchImage@2x.png",@"480,320,2,8,p", // ios 6,7,8 - iphone 4/4s - portrait
@"LaunchImage@2x.png",@"480,320,2,8,l", // ios 6,7,8 - iphone 4/4s - landscape
@"LaunchImage-800-667h@2x.png",@"667,375,2,8,p", // ios 8 - iphone 6 - portrait
@"LaunchImage-800-667h@2x.png",@"667,375,2,8,l", // ios 8 - iphone 6 - landscape
@"LaunchImage-800-Portrait-736h@3x.png",@"736,414,3,8,p", // ios 8 - iphone 6 plus - portrait
@"LaunchImage-800-Landscape-736h@3x.png",@"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;
}
Шаг 2. Выше метод должен работать, добавив следующий код также в той же категории 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;
}
Я понимаю, что это не обязательно лучшее решение для всех, но самый простой (и наименее подверженный ошибкам, IMHO) способ сделать это, сделав отдельную запись в каталоге Images.xcassets. Я назвал это SplashImage
,
Когда вы собираетесь добавить новую запись, убедитесь, что вы не выбрали "New Launch Image" в качестве опции. Вместо этого выберите общий "Новый набор изображений". Затем откройте инспектора и выберите соответствующие параметры. Если вы строите только для устройств Retina, как я, вы можете выбрать следующее:
Это оставит вас с четырьмя записями (iPhone 4S, iPhone 5(s,c), iPhone 6 и iPhone 6 Plus).
Файлы, соответствующие изображениям, являются следующими:
| 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 |
Конечно, после того, как вы сделали это, вы можете просто использовать [UIImage imageNamed:@"SplashImage"]
С помощью ответа Пичиричи я реализовал следующую категорию (iOS 7+): UIImage + AssetLaunchImage
На самом деле это немного больше, чем генерирование имени на лету, но, вероятно, будет полезно.
Обновлено до последнего синтаксиса Swift (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
}