iOS 7 / Xcode 5: Access device launch images programmatically
有什么方法可以将应用程序LaunchImage用作通用iOS应用程序中的背景,而无需将相同的图像资源放在多个位置?
我无法访问
尽管此变通办法可以完成工作,但我不希望将每个图像两次包含到应用程序中。 这也具有高的维护成本。
感谢有关如何访问当前设备的LaunchImage的任何建议。
对于iOS <7,GCOLaunchImageTransition必须已完成此工作。
您可以复制/粘贴以下代码以在运行时加载应用程序的启动映像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Load launch image NSString *launchImageName; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { if ([UIScreen mainScreen].bounds.size.height == 480) launchImageName = @"[email protected]"; // iPhone 4/4s, 3.5 inch screen if ([UIScreen mainScreen].bounds.size.height == 568) launchImageName = @"[email protected]"; // iPhone 5/5s, 4.0 inch screen if ([UIScreen mainScreen].bounds.size.height == 667) launchImageName = @"[email protected]"; // iPhone 6, 4.7 inch screen if ([UIScreen mainScreen].bounds.size.height == 736) launchImageName = @"[email protected]"; // 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]]; |
您可以使用启动映像,而不必两次包含它们。关键是,当您使用资产目录时,应用程序捆绑包中包含的图像的文件名是(某种)标准化的,并且可能与您命名原始文件的内容无关。
特别是,当您使用LaunchImage图像集时,最终出现在应用程序捆绑包中的文件的名称如下
- LaunchImage.png
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- LaunchImage-700-Landscape@2x~ipad.png
请特别注意,它们未命名为
因此
该文档表明
因此,根据您的实际情况,您可能需要进行一些反复试验才能获得正确的文件名。在模拟器中构建并运行该应用程序,然后您始终可以在
但是,再次要点是,无需包含图像文件的重复项,并且您无需对
大多数答案都需要根据设备类型,规模,大小等来创建映像名称。但是正如Matthew Burke指出的那样,启动映像目录中的每个映像都将重命名为" LaunchImage *",因此我们可以迭代启动过程图像并找到(对于当前设备)合适的图像。在Objective-C中,它可能看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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")
Daniel Witurna出色答案的Swift版本,不需要检查所有已知设备类型或方向的列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 } |
下面是我在iOS 7.0+中测试时的结果,仅纵向调整:
1 2 3 4 5 6 | 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 |
捆绑软件中的
目标C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | - (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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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中的一个简洁函数,用于在运行时获取启动映像名称:
1 2 3 4 5 6 7 8 9 10 11 | func launchImageName() -> String { switch (UI_USER_INTERFACE_IDIOM(), UIScreen.mainScreen().scale, UIScreen.mainScreen().bounds.size.height) { case (.Phone, _, 480): return"[email protected]" case (.Phone, _, 568): return"[email protected]" case (.Phone, _, 667): return"[email protected]" case (.Phone, _, 736): return"[email protected]" case (.Pad, 1, _): return"LaunchImage-700-Portrait~ipad.png" case (.Pad, 2, _): return"LaunchImage-700-Portrait@2x~ipad.png" default: return"LaunchImage" } } |
Matthew Burke的答案是正确的答案。以下是我用来使此代码在iOS9 / Xcode7上运行,为iOS7及更高版本,为iPhone和iPad生成应用程序的应用允许使用的代码。
首先,详细说明一下:
在iOS8 / Xcode6中,如果您使用情节提要启动画面文件,则在应用启动时,该应用会以适合您设备的正确分辨率创建该启动画面文件的2张图像(一幅肖像,一张风景),并且您能够文件路径中的该图像。 (我相信它存储在Library / LaunchImage文件夹中)。
但是,在iOS9 / XCode 7中,不再创建该映像(尽管快照快照是在快照文件夹中拍摄的,但是其名称始终保持更改,因此不具描述性),因此,如果要在代码的其他位置使用LaunchImage,您将必须使用启动图像源(最好是通过资产目录,因为应用程序稀疏)。现在,正如马修·伯克(Matthew Burke)解释的那样,您不能仅仅通过以下方式获得该图像:
1 | let launchImage = UIImage(named:"LaunchImage") |
即使资产目录中的图像名称是LaunchImage,Xcode / iOS9也不允许您使用。
幸运的是,您不必在资产目录中再次包含启动映像。我要说的是幸运的,因为如果您要为所有设备制作一个应用程序,那将意味着您的应用程序下载大小增加约20MB。
那么,如何获得那些启动图像呢?好吧,这是步骤:
然后,在您的代码中,您需要确定您的应用程序正在运行的设备,以及它是纵向还是横向的,然后确定要使用的图像。为了使操作更简单,我使用了以下框架:https://github.com/InderKumarRathore/DeviceGuru。请注意,它尚未包括最新的设备(iPhone 6s和iPhone 6s plus),因此您必须为此在他的快速文件中添加一行。然后,将以下代码放在vc中您想要启动图像的位置,然后进入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | 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 ="[email protected]" let HD40 ="LaunchImage-700-568h@2x" let HD47 ="[email protected]" var HD55 ="[email protected]" 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 ="[email protected]" 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 } |
基于Daniel的出色回答,另一个更现代,更优雅的解决方案:
1 2 3 4 5 6 7 8 9 10 | 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 } } |
这样,您可以编写:
1 | let myLaunchImage = UIImage.launchImage |
这是基于Daniel Witurna解决方案的修改后的代码。此代码段使用谓词从捆绑包图像列表中过滤启动图像名称。谓词将潜在地避免循环的次数,以从一系列图像路径中过滤启动图像。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | -(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; } |
我不知道这是否意味着通过代码访问。但是,如果选择"项目"->"目标"->"构建阶段"->"复制捆绑包资源",则单击" +"并"添加其他",导航到Images.xcassets-> LaunchImage.launchimage并选择要使用的png然后点击"打开"。现在您可以使用
如果您需要确定设备,我可以使用以下代码(虽然有点麻烦,但可以解决问题)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 | if (IS_IPHONE_4_OR_LESS) { self.imageView.image = [UIImage imageNamed:@"[email protected]"]; } else if (IS_IPHONE_5){ self.imageView.image = [UIImage imageNamed:@"[email protected]"]; } else if (IS_IPHONE_6){ self.imageView.image = [UIImage imageNamed:@"[email protected]"]; } else if (IS_IPHONE_6P){ self.imageView.image = [UIImage imageNamed:@"[email protected]"]; } |
创建
如果您支持iOS5和iOS6,这将省去很多麻烦。
"文件夹" /类别的实际名称将跟随构建的资产。
马修·伯克(Matthew Burke)所说的一切都是真实的;)
在项目中创建一个没有任何物理目录支持的新组。直接从
由于" LaunchImage"资产实际上是自定义的野兽……
我的建议是用图像(或您实际需要的子集)的副本创建二级资产目录。
我称之为FauxLaunchImage。他们可以像您想要的那样访问它
1 | [UIImage imageNamed:@"FauxLaunchImage"]; |