41

Is there any way to use the apps LaunchImage as a background in an universal iOS app without putting the same image resources in multiple places?

I wasn't able to access the LaunchImage files in Images.xcassets, so I created two new Image Sets "Background Portrait" and "Background Landscape" (since there seems to be no way to put landscape and portrait images into the same set).

While this workaround does the jobs, I would hate to include every image into the app twice. This also has a high maintenance cost.

Any advice on how to access the LaunchImage for the current device is appreciated.

GCOLaunchImageTransition must have done the job for iOS < 7.

4

16 回答 16

56

您可以复制/粘贴以下代码以在运行时加载应用的启动图像:

// Load launch image
NSString *launchImageName;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
    if ([UIScreen mainScreen].bounds.size.height == 480) launchImageName = @"LaunchImage-700@2x.png"; // iPhone 4/4s, 3.5 inch screen
    if ([UIScreen mainScreen].bounds.size.height == 568) launchImageName = @"LaunchImage-700-568h@2x.png"; // iPhone 5/5s, 4.0 inch screen
    if ([UIScreen mainScreen].bounds.size.height == 667) launchImageName = @"LaunchImage-800-667h@2x.png"; // iPhone 6, 4.7 inch screen
    if ([UIScreen mainScreen].bounds.size.height == 736) launchImageName = @"LaunchImage-800-Portrait-736h@3x.png"; // iPhone 6+, 5.5 inch screen
}
else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
    if ([UIScreen mainScreen].scale == 1) launchImageName = @"LaunchImage-700-Portrait~ipad.png"; // iPad 2
    if ([UIScreen mainScreen].scale == 2) launchImageName = @"LaunchImage-700-Portrait@2x~ipad.png"; // Retina iPads
}
self.launchImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:launchImageName]];
于 2014-12-05T17:44:33.683 回答
42

您可以使用启动图像而不必包含它们两次。关键是,当您使用资产目录时,应用程序包中包含的图像的文件名是(某种)标准化的,可能与您命名的原始文件无关。

特别是,当您使用 LaunchImage 图像集时,最终在应用程序包中的文件具有如下名称

  • 启动图像.png
  • LaunchImage@2x.png
  • LaunchImage-700@2x.png
  • LaunchImage-568h@2x.png
  • LaunchImage-700-568h@2x.png
  • LaunchImage-700-Landscape@2x~ipad.png

等等。请特别注意,它们没有被命名Default.png或任何变体。即使这就是你所说的文件。一旦您将它们放入资产目录中的一个井中,它们就会以标准名称从另一端出来。

所以[UIImage imageNamed:@"Default"]不会工作,因为应用程序包中没有这样的文件。但是,[UIImage imageNamed:@"LaunchImage"]会起作用(假设您已经很好地填充了 iPhone Portrait 2x 或iOS7 之前的iPhone Portrait 1x)。

该文档表明该imageNamed:方法UIImage应该自动神奇地选择正确的版本,但我认为这仅适用于启动图像以外的图像集 - 至少我没有让它完全正常工作(可能只是我没有做正确的事)。

因此,根据您的具体情况,您可能需要进行一些试验和错误才能获得正确的文件名。在模拟器中构建并运行应用程序,然后您可以随时查看相应的子目录~/Library/Application Support/iPhone Simulator以验证应用程序包中的实际文件名是什么。

但同样,重点是不需要包含图像文件的副本,也不需要对Copy Bundle Resources构建阶段进行任何调整。

于 2013-10-23T01:48:10.497 回答
41

大多数答案需要根据设备类型、比例、大小等创建图像名称。但正如 Matthew Burke 指出的那样,启动图像目录中的每个图像都将重命名为“LaunchImage*”,因此我们能够迭代我们的启动图像并找到(对于当前设备)合适的图像。在 Objective-C 中,它可能看起来像这样:

NSArray *allPngImageNames = [[NSBundle mainBundle] pathsForResourcesOfType:@"png"
                                        inDirectory:nil];

for (NSString *imgName in allPngImageNames){
    // Find launch images
    if ([imgName containsString:@"LaunchImage"]){
        UIImage *img = [UIImage imageNamed:imgName];
        // Has image same scale and dimensions as our current device's screen?
        if (img.scale == [UIScreen mainScreen].scale && CGSizeEqualToSize(img.size, [UIScreen mainScreen].bounds.size)) {
            NSLog(@"Found launch image for current device %@", img.description);
            break;
        }
    }
}

(请注意,此代码使用 iOS 8 引入的“containsString”方法。对于以前的 iOS 版本,请使用“rangeOfString”)

于 2014-10-17T12:26:49.523 回答
10

下面是我在 iOS 7.0+ 中测试的结果,只有纵向定位:

3.5 inch screen: LaunchImage-700@2x.png
4.0 inch screen: LaunchImage-700-568h@2x.png
4.7 inch screen: LaunchImage-800-667h@2x.png
5.5 inch screen: LaunchImage-800-Portrait-736h@3x.png
iPad2          : LaunchImage-700-Portrait~ipad.png
Retina iPads   : LaunchImage-700-Portrait@2x~ipad.png
于 2014-09-15T08:38:01.840 回答
10

Daniel Witurna 的出色答案的 Swift 版本,不需要检查所有已知设备类型或方向的列表。

func appLaunchImage() -> UIImage? {

        let allPngImageNames = Bundle.main.paths(forResourcesOfType: "png", inDirectory: nil)

        for imageName in allPngImageNames
        {
            // make sure that the image name contains the string 'LaunchImage' and that we can actually create a UIImage from it.

            guard
                imageName.contains("LaunchImage"), 
                let image = UIImage(named: imageName) 
                else { continue }

            // if the image has the same scale AND dimensions as the current device's screen...

            if (image.scale == UIScreen.main.scale) && (image.size.equalTo(UIScreen.main.bounds.size))
            {
                return image
            }
        }

        return nil
    }
于 2016-07-19T13:22:21.973 回答
8

Info.plist在 bundle 中包含启动映像信息,包括启动映像的名称。

目标-C:

- (UIImage *)getCurrentLaunchImage {
    CGSize screenSize = [UIScreen mainScreen].bounds.size;

    NSString *interfaceOrientation = nil;
    if (([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown) ||
        ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait)) {
        interfaceOrientation = @"Portrait";
    } else {
        interfaceOrientation = @"Landscape";
    }

    NSString *launchImageName = nil;

    NSArray *launchImages = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
    for (NSDictionary *launchImage in launchImages) {
        CGSize launchImageSize = CGSizeFromString(launchImage[@"UILaunchImageSize"]);
        NSString *launchImageOrientation = launchImage[@"UILaunchImageOrientation"];

        if (CGSizeEqualToSize(launchImageSize, screenSize) &&
            [launchImageOrientation isEqualToString:interfaceOrientation]) {
            launchImageName = launchImage[@"UILaunchImageName"];
            break;
        }
    }

    return [UIImage imageNamed:launchImageName];
}

斯威夫特 4:

func getCurrentLaunchImage() -> UIImage? {

    guard let launchImages = Bundle.main.infoDictionary?["UILaunchImages"] as? [[String: Any]] else { return nil }

    let screenSize = UIScreen.main.bounds.size

    var interfaceOrientation: String
    switch UIApplication.shared.statusBarOrientation {
    case .portrait,
         .portraitUpsideDown:
        interfaceOrientation = "Portrait"
    default:
        interfaceOrientation = "Landscape"
    }

    for launchImage in launchImages {

        guard let imageSize = launchImage["UILaunchImageSize"] as? String else { continue }
        let launchImageSize = CGSizeFromString(imageSize)

        guard let launchImageOrientation = launchImage["UILaunchImageOrientation"] as? String else { continue }

        if
            launchImageSize.equalTo(screenSize),
            launchImageOrientation == interfaceOrientation,
            let launchImageName = launchImage["UILaunchImageName"] as? String {
            return UIImage(named: launchImageName)
        }
    }

    return nil
}
于 2018-01-04T09:09:57.300 回答
2

Swift 中用于在运行时获取启动图像名称的简洁函数:

func launchImageName() -> String {
    switch (UI_USER_INTERFACE_IDIOM(), UIScreen.mainScreen().scale, UIScreen.mainScreen().bounds.size.height) {
        case (.Phone, _, 480): return "LaunchImage-700@2x.png"
        case (.Phone, _, 568): return "LaunchImage-700-568h@2x.png"
        case (.Phone, _, 667): return "LaunchImage-800-667h@2x.png"
        case (.Phone, _, 736): return "LaunchImage-800-Portrait-736h@3x.png"
        case (.Pad, 1, _): return "LaunchImage-700-Portrait~ipad.png"
        case (.Pad, 2, _): return "LaunchImage-700-Portrait@2x~ipad.png"
        default: return "LaunchImage"
    }
}
于 2015-09-10T09:07:00.800 回答
1

如果您需要确定设备,我使用以下代码(它有点快速和肮脏,但它可以解决问题)

if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ){

    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    if( screenHeight < screenWidth ){
        screenHeight = screenWidth;
    }

    if( screenHeight > 480 && screenHeight < 667 ){
        DLog(@"iPhone 5/5s");
    } else if ( screenHeight > 480 && screenHeight < 736 ){
        DLog(@"iPhone 6");
    } else if ( screenHeight > 480 ){
        DLog(@"iPhone 6 Plus");
    } else {
        DLog(@"iPhone 4/4s");
    }
}
于 2014-10-16T09:23:32.273 回答
1

我不知道这是否是您通过代码访问的意思。但是,如果您选择“项目->目标->构建阶段->复制捆绑资源”,则单击“+”并“添加其他”导航到您的 Images.xcassets->LaunchImage.launchimage 并选择您要使用的任何 png并点击“打开”。现在您可以使用图像[UIImage imageNamed:@"Default"];

于 2013-10-16T20:38:06.837 回答
1

基于Daniel的出色回答的另一个更现代和优雅的解决方案:

extension UIImage {
    static var launchImage: UIImage? {
        let pngs = Bundle.main.paths(forResourcesOfType: "png", inDirectory: nil)
        return pngs
            .filter({$0.contains("LaunchImage")})
            .compactMap({UIImage(named: $0)})
            .filter({$0.size == UIScreen.main.bounds.size})
            .first
    } 
}

这样你就可以写:

let myLaunchImage = UIImage.launchImage
于 2019-04-26T14:21:24.367 回答
1

Matthew Burke's answer is the correct answer. Below is the code I'm using to get this working for iOS9 / Xcode7, building an app for iOS7 and up, for iPhone and iPad, landscape allowed.

First, to elaborate a bit more: In iOS8 / Xcode6, if you were using a storyboard Launch Screen File, on app startup, the app would create 2 images (one portrait, one landscape) of that launch screen file in the correct resolution for your device and you were able to get that image from the file path. (I believe it was stored in Library/LaunchImage folder).

However in iOS9/XCode 7 this image is not created anymore (although a snapshot is taken in the snapshots folder, but that has a undescriptive name that changes all the time), so if you want to use your LaunchImage somewhere else in your code, you'll have to use a Launch Image Source (through asset catalog preferably, because of App Thinning). Now, as Matthew Burke is explaining you can't get to that image just by doing:

let launchImage = UIImage(named: "LaunchImage")

Even though the image name in your asset catalog is LaunchImage, Xcode/iOS9 won't let you.

Luckily you don't have to include your launch images again in your asset catalog. I'm saying luckily because that would mean about a 20MB increase of your App Download size if you're making an app for all devices.

So, how to get to those launch images than? Well, here are the steps:

  1. Create your launch images and put them in your asset catalog. Name of the images is not important.
  2. Make sure your Launch Screen File (under your target's general settings) is empty and remove your app from your device and simulator. (just deleting the filename and re-running won't do it, you'll have to remove your app first)
  3. Run your app in the simulator and go to the ~/Library/Application Support/iPhone Simulator folder and find your app there. (It's a bit of a hassle as the folder names are indescriptive.) Show the package content for your .app file and in there you'll see several image files starting with "LaunchImage- ..." In my case there were 9 images as I'm making an app for iPhone and iPad for iOS7 and up.
  4. Then, in your code you'll need to determine what device your app is running on and if it's in portrait or landscape and then decide which image to use. To make this a bit easier I used this framework: https://github.com/InderKumarRathore/DeviceGuru . Be ware, it didn't include the latest devices yet (iPhone 6s and iPhone 6s plus) so you'll have to add a line in his swift file for that. Then, put below piece of code in the vc where you want your launchImage and there you go:

    func launchImage() -> UIImage? {
        if let launchImageName = launcheImageName() {
            print(launchImageName)
            return UIImage(named: launchImageName)
        }
        else {
            print("no launch image")
            return nil
        }
    }
    
    func launcheImageName() -> String? {
        let HD35 = "LaunchImage-700@2x.png"
        let HD40 = "LaunchImage-700-568h@2x"
        let HD47 = "LaunchImage-800-667h@2x.png"
        var HD55 = "LaunchImage-800-Portrait-736h@3x.png"
        var padHD = "LaunchImage-700-Portrait@2x~ipad.png"
        var pad = "LaunchImage-700-Portrait~ipad.png"
    
        if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft || UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight {
            HD55 = "LaunchImage-800-Landscape-736h@3x.png"
            padHD = "LaunchImage-700-Landscape@2x~ipad.png"
            pad = "LaunchImage-700-Landscape~ipad.png"
        }
    
        let hardware = hardwareString()
        if (hardware == "iPhone1,1")            { return HD35 }
        if (hardware == "iPhone1,2")            { return HD35 }
        if (hardware == "iPhone2,1")            { return HD35 }
        if (hardware == "iPhone3,1")            { return HD35 }
        if (hardware == "iPhone3,2")            { return HD35 }
        if (hardware == "iPhone3,3")            { return HD35 }
        if (hardware == "iPhone4,1")            { return HD35 }
        if (hardware == "iPhone5,1")            { return HD40 }
        if (hardware == "iPhone5,2")            { return HD40 }
        if (hardware == "iPhone5,3")            { return HD40 }
        if (hardware == "iPhone5,4")            { return HD40 }
        if (hardware == "iPhone6,1")            { return HD40 }
        if (hardware == "iPhone6,2")            { return HD40 }
        if (hardware == "iPhone7,1")            { return HD55 }
        if (hardware == "iPhone7,2")            { return HD47 }
        if (hardware == "iPhone8,1")            { return HD55 }
        if (hardware == "iPhone8,2")            { return HD47 }
    
        if (hardware == "iPod1,1")              { return HD35 }
        if (hardware == "iPod2,1")              { return HD35 }
        if (hardware == "iPod3,1")              { return HD35 }
        if (hardware == "iPod4,1")              { return HD35 }
        if (hardware == "iPod5,1")              { return HD40 }
    
        if (hardware == "iPad1,1")              { return pad }
        if (hardware == "iPad1,2")              { return pad }
        if (hardware == "iPad2,1")              { return pad }
        if (hardware == "iPad2,2")              { return pad }
        if (hardware == "iPad2,3")              { return pad }
        if (hardware == "iPad2,4")              { return pad }
        if (hardware == "iPad2,5")              { return pad }
        if (hardware == "iPad2,6")              { return pad }
        if (hardware == "iPad2,7")              { return pad }
        if (hardware == "iPad3,1")              { return padHD }
        if (hardware == "iPad3,2")              { return padHD }
        if (hardware == "iPad3,3")              { return padHD }
        if (hardware == "iPad3,4")              { return padHD }
        if (hardware == "iPad3,5")              { return padHD }
        if (hardware == "iPad3,6")              { return padHD }
        if (hardware == "iPad4,1")              { return padHD }
        if (hardware == "iPad4,2")              { return padHD }
        if (hardware == "iPad4,3")              { return padHD }
        if (hardware == "iPad4,4")              { return padHD }
        if (hardware == "iPad4,5")              { return padHD }
        if (hardware == "iPad4,6")              { return padHD }
        if (hardware == "iPad4,7")              { return padHD }
        if (hardware == "iPad4,8")              { return padHD }
        if (hardware == "iPad5,3")              { return padHD }
        if (hardware == "iPad5,4")              { return padHD }
    
        if (hardware == "i386")                 { return HD55 }
        if (hardware == "x86_64")               { return HD55 }
        if (hardware.hasPrefix("iPhone"))       { return HD55 }
        if (hardware.hasPrefix("iPod"))         { return HD55 }
        if (hardware.hasPrefix("iPad"))         { return padHD }
    
        //log message that your device is not present in the list
        logMessage(hardware)
    
        return nil
    }
    
于 2015-09-22T02:12:34.670 回答
1

这是基于 Daniel Witurna 解决方案的修改代码。此代码片段使用谓词从捆绑图像列表中过滤启动图像名称。谓词可能会避免循环数以从图像路径数组中过滤启动图像。

-(NSString *)getLaunchImageName{

NSArray *allPngImageNames = [[NSBundle mainBundle] pathsForResourcesOfType:@"png" inDirectory:nil];
NSString *expression=[NSString stringWithFormat:@"SELF contains '%@'",@"LaunchImage"];

NSArray *res = [allPngImageNames filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:expression]];

NSString *launchImageName;
for (launchImageName in res){
    {
        UIImage *img = [UIImage imageNamed:launchImageName];
        // Has image same scale and dimensions as our current device's screen?
        if (img.scale == [UIScreen mainScreen].scale && CGSizeEqualToSize(img.size, [UIScreen mainScreen].bounds.size)) {
            NSLog(@"Found launch image for current device %@", img.description);
            break;
        }

    }

}
return launchImageName; }
于 2017-02-14T08:55:02.767 回答
0

创建 后Images.xcassets,只需将其重命名LaunchImageDefault.

如果你支持 iOS5 和 iOS6,这会省去很多麻烦。

“文件夹”/类别的实际名称将在构建时跟随到资产中。马修伯克所说的一切都是真的;)

于 2014-02-07T01:24:51.383 回答
0

在您的项目中创建一个新组,不受任何物理目录的支持。将您的启动图像导入该组,直接从LaunchImage.launchimage. 瞧。

于 2015-03-10T22:52:50.283 回答
0
 if (IS_IPHONE_4_OR_LESS) {
    self.imageView.image = [UIImage imageNamed:@"LaunchImage-700@2x.png"];
}
else if (IS_IPHONE_5){
     self.imageView.image = [UIImage imageNamed:@"LaunchImage-700-568h@2x.png"];
}
else if (IS_IPHONE_6){
     self.imageView.image = [UIImage imageNamed:@"LaunchImage-800-667h@2x.png"];
}
else if (IS_IPHONE_6P){
      self.imageView.image = [UIImage imageNamed:@"LaunchImage-800-Portrait-736h@3x.png"];
}
于 2016-05-31T13:08:35.360 回答
-1

由于“LaunchImage”资产实际上是一个自定义的野兽……</p>

我的建议是创建一个二级资产目录,其中包含图像的副本(或您实际需要的子集)。

我称我为 FauxLaunchImage。他们你可以像你想要的那样访问它

[UIImage imageNamed:@"FauxLaunchImage"];
于 2014-04-02T17:15:36.590 回答