【问题标题】:iOS 7 / Xcode 5: Access device launch images programmaticallyiOS 7 / Xcode 5:以编程方式访问设备启动图像
【发布时间】:2013-10-24 23:02:24
【问题描述】:

有没有什么方法可以在通用 iOS 应用中使用应用 LaunchImage 作为背景,而无需将相同的图像资源放在多个位置?

我无法访问Images.xcassets 中的LaunchImage 文件,因此我创建了两个新的图像集“背景肖像”和“背景风景”(因为似乎没有办法放置风景和肖像图像进入同一组)。

虽然这种解决方法可以解决问题,但我不想将每张图片都包含到应用程序中两次。这也有很高的维护成本。

感谢任何有关如何访问当前设备的 LaunchImage 的建议。

GCOLaunchImageTransition 必须为 iOS

【问题讨论】:

  • 嗨蒂姆,你找到解决这个问题的方法了吗?我也有同样的问题。
  • 刚刚尝试了 GCOLaunchImageTransition,似乎仍然有效。

标签: ios objective-c xcode ios7


【解决方案1】:

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

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

【讨论】:

  • 谢谢。这个解决方案对我有用,不像 Matthew Burke 接受的答案
【解决方案2】:

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

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

  • 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 或 pre iOS7 iPhone Portrait 1x)。

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

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

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

【讨论】:

  • 我赞成这个答案......但我也有兴趣能够在我的 .xib 中看到图像,并在我的 .xib 中选择“LaunchImage”在设备上工作(并且可能在模拟器),但只显示 .xib 中的蓝色大问号。这部分有什么简单的解决方案吗?
  • 那么,有人让 [UIImage imageNamed:@"LaunchImage"] 工作了吗?
  • 对于我的 tamarin 应用,不幸的是,它没有
  • 哪个是哪个设备的?
  • @SebastiaandeWeert 在最新的 Xcode/SDK 上似乎 iPhone X 的启动画面被命名为LaunchImage-1100-Portrait-2436h
【解决方案3】:

大多数答案需要根据设备类型、比例、大小等创建图像名称。但正如 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”)

【讨论】:

  • 我认为,为了避免每次都搜索,生成的 imgName(或其 lastPathComponent)可以存储为本地用户默认值。
  • @Rolleric 我考虑了你的建议,可能你是对的。定义的启动图像将始终具有相同的名称,即使您更改它,并且用户默认值是设备绑定的,所以这也应该没问题。但是,我不确定搜索的成本有多大。可能只是速度和存储之间的权衡。
  • imageNamed 在 iOS 7 上对我不起作用 - 您可以使用 imageWithContentsOfFile
  • 这是迄今为止最好的解决方案
  • 一个简单的优化是在找到匹配项时跳出 for 循环。
【解决方案4】:

以下是我在 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

【讨论】:

    【解决方案5】:

    Daniel Witurna 的出色 answer 的 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
        }
    

    【讨论】:

    • 虽然 xcode(谢天谢地)现代化了一堆命名约定,但这个基础在 xcode 9、swift 3、iOS 10.3 中非常出色(用于保持从应用程序启动 LaunchImage 到用户第一次点击的无缝“书籍封面” )。谢谢!! :)
    【解决方案6】:

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

    目标-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
    }
    

    【讨论】:

    • Swift 4:这适用于纵向,但不适用于横向。您需要将 screenSize 更改为以下行:让 screenSize = UIScreen.main.fixedCoordinateSpace.bounds.size。添加了带有修复功能的 Swift 5
    【解决方案7】:

    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"
        }
    }
    

    【讨论】:

      【解决方案8】:

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

      【讨论】:

      • 虽然“Images.xcassets”已经在“复制捆绑资源”部分,但单击 + 添加单个文件时我看不到它。而目前“[UIImage imageNamed:@"Default"];"对我不起作用。
      • 您需要先单击“+”,然后单击“添加其他”,然后导航到您的 Images.xcassets->LaunchImage.launchimage。 Images.xcassets 只是一个文件夹。
      【解决方案9】:

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

      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");
          }
      }
      

      【讨论】:

        【解决方案10】:

        Matthew Burke 的答案是正确答案。下面是我用来让它在 iOS9 / Xcode7 上工作的代码,为 iOS7 及更高版本构建一个应用程序,为 iPhone 和 iPad,允许横向。

        首先,详细说明一下: 在 iOS8 / Xcode6 中,如果您使用的是故事板启动屏幕文件,则在应用启动时,该应用将以适合您设备的正确分辨率创建该启动屏幕文件的 2 张图像(一张纵向,一张横向),并且您能够获得该图像来自文件路径。 (我相信它存储在 Library/LaunchImage 文件夹中)。

        但是在 iOS9/XCode 7 中不再创建这个图像(虽然快照是在快照文件夹中拍摄的,但它的名称一直在变化),所以如果你想在其他地方使用你的 LaunchImage您的代码,您必须使用启动图像源(最好通过资产目录,因为 App Thinning)。现在,正如 Matthew Burke 解释的那样,您无法仅通过以下方式获得该图像:

        let launchImage = UIImage(named: "LaunchImage")
        

        即使资产目录中的图像名称是 LaunchImage,Xcode/iOS9 也不会允许。

        幸运的是,您不必在资产目录中再次包含启动图像。我说的是幸运,因为如果您要为所有设备制作应用程序,这将意味着您的应用程序下载大小增加约 20MB。

        那么,如何获得那些启动图像呢?好了,步骤如下:

        1. 创建您的启动图像并将它们放入您的资产目录中。图片的名称并不重要。
        2. 确保您的启动屏幕文件(在您的目标的常规设置下)为空,然后从您的设备和模拟器中删除您的应用程序。 (只是删除文件名并重新运行不会这样做,您必须先删除您的应用)
        3. 在模拟器中运行您的应用程序并转到 ~/Library/Application Support/iPhone Simulator 文件夹并在那里找到您的应用程序。 (这有点麻烦,因为文件夹名称无法描述。)显示你的 .app 文件的包内容,在那里你会看到几个以“LaunchImage-...”开头的图像文件在我的例子中,有 9 个图像因为我正在为 iOS7 及更高版本的 iPhone 和 iPad 制作应用程序。
        4. 然后,在您的代码中,您需要确定您的应用在什么设备上运行以及它是纵向还是横向,然后决定使用哪个图像。为了让这更容易,我使用了这个框架:https://github.com/InderKumarRathore/DeviceGuru。请注意,它还没有包含最新的设备(iPhone 6s 和 iPhone 6s plus),所以你必须在他的 swift 文件中为此添加一行。然后,将下面的代码放入您想要launchImage的vc中,然后就可以了:

          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
          }
          

        【讨论】:

        • 哇,这很烦人,但感谢您分享您的答案。确认这行得通...
        【解决方案11】:

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

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

        【讨论】:

          【解决方案12】:

          另一个基于 Daniel 的 excellent answer 的更现代和优雅的解决方案:

          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
          

          【讨论】:

            【解决方案13】:

            创建Images.xcassets 后,只需将LaunchImage 重命名为Default

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

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

            【讨论】:

              【解决方案14】:

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

              【讨论】:

                【解决方案15】:
                 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"];
                }
                

                【讨论】:

                  【解决方案16】:

                  因为“LaunchImage”资源实际上是一个自定义的野兽……

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

                  我称我为 FauxLaunchImage。他们可以随心所欲地访问它

                  [UIImage imageNamed:@"FauxLaunchImage"];
                  

                  【讨论】:

                  • 这个解决方案没有回答这个问题,因为它被要求访问 LaunchImage 而没有重复的资产。
                  • 我的回答是否定的。你不应该尝试访问启动图像。如果你现在知道如何访问它,Apple 很可能会从你的手下改变它。如果一个屏幕大小的图像正在扼杀您的应用程序大小,则说明其他问题。
                  猜你喜欢
                  • 2015-11-01
                  • 2013-09-16
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-10-11
                  • 2014-01-26
                  • 1970-01-01
                  相关资源
                  最近更新 更多