Creating iOS splash screens with Visual Studio Tools for Apache Cordova

When you create a Cordova project with Visual Studio Tools for Apache Cordova it generates a set of splash screens for all supported iOS (both iPhone and iPad) resolutions. These are found under res\screens\ios.

Visual Studio project showing the splash screen images.
Visual Studio project showing the splash screen images.
XCode project showing the splash screen images.
XCode project showing the splash screen images.

There are two things to notice here:

  • First, the names in Visual Studio are like screen-iphone-portrait.png or screen-ipad-portrait.png and in XCode are like Default~iphone.png or Default-Portrait~ipad.png.
  • Second, if you actually look at the files you’ll see they are not the same. The splash screens generated in VS are ignored and default Cordova images are used.
Default Visual Studio splash screen image for iPhone.
Default Visual Studio splash screen image for iPhone.
Default Cordova splash screen image for iPhone.
Default Cordova splash screen image for iPhone.

You can see the default Cordova splash screen image in the iOS built if you open the generated XCode project on the Mac where the iOS app is built.

Splash screen image in the XCode project generated by Cordova.
Splash screen image in the XCode project generated by Cordova.

The questions are: why are the file names different in Visual Studio and XCode, and why are the images not correctly copied from Visual Studio project to XCode project.

I’ll start with the second question. The problem here is that just because the files are in the Visual Studio project does not mean much. If you open the Cordova configuration file, config.xml, it only contains this (for a newly created blank project):

<?xml version='1.0' encoding='utf-8'?>
<widget xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:vs="http://schemas.microsoft.com/appx/2014/htmlapps" id="io.cordova.BlankCordovaApp1" version="1.0.0" xmlns="http://www.w3.org/ns/widgets">
  <name>BlankCordovaApp1</name>
  <description>A blank project that uses Apache Cordova to help you build an app that targets multiple mobile platforms: Android, iOS, Windows, and Windows Phone.</description>
  <author href="http://cordova.io" email="dev@cordova.apache.org">Apache Cordova Team </author>
  <content src="index.html" />
  <access origin="*" />
  <vs:features />
  <preference name="SplashScreen" value="screen" />
  <preference name="windows-target-version" value="8.0" />
  <preference name="windows-phone-target-version" value="8.1" />
</widget>

However, if you look at the Cordova documentation for images and splashes you’ll see the config.xml file should contain something like this:

<platform name="ios">
    <!-- images are determined by width and height. The following are supported -->
    <splash src="res/screen/ios/Default~iphone.png" width="320" height="480"/>
    <splash src="res/screen/ios/Default@2x~iphone.png" width="640" height="960"/>
    <splash src="res/screen/ios/Default-Portrait~ipad.png" width="768" height="1024"/>
    <splash src="res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" height="2048"/>
    <splash src="res/screen/ios/Default-Landscape~ipad.png" width="1024" height="768"/>
    <splash src="res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" height="1536"/>
    <splash src="res/screen/ios/Default-568h@2x~iphone.png" width="640" height="1136"/>
    <splash src="res/screen/ios/Default-667h.png" width="750" height="1334"/>
    <splash src="res/screen/ios/Default-736h.png" width="1242" height="2208"/>
    <splash src="res/screen/ios/Default-Landscape-736h.png" width="2208" height="1242"/>
</platform>

The names here look like the ones that appear in the XCode project. But key is the platform element with its splash children. We have to add these to the config.xml file, but it can only be done by manually changing the configuration file, as Visual Studio Tools for Apache Cordova do not support this in the IDE yet.

<?xml version='1.0' encoding='utf-8'?>
<widget xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:vs="http://schemas.microsoft.com/appx/2014/htmlapps" id="io.cordova.BlankCordovaApp1" version="1.0.0" xmlns="http://www.w3.org/ns/widgets">
  <name>BlankCordovaApp1</name>
  <description>A blank project that uses Apache Cordova to help you build an app that targets multiple mobile platforms: Android, iOS, Windows, and Windows Phone.</description>
  <author href="http://cordova.io" email="dev@cordova.apache.org">Apache Cordova Team </author>
  <content src="index.html" />
  <access origin="*" />
  <vs:features />
  <preference name="SplashScreen" value="screen" />
  <preference name="windows-target-version" value="8.0" />
  <preference name="windows-phone-target-version" value="8.1" />
  
  <platform name="ios">
    <splash src="res/screens/ios/screen-iphone-portrait.png" width="320" height="480"/>
    <splash src="res/screens/ios/screen-iphone-portrait-2x.png" width="640" height="960"/>
    <splash src="res/screens/ios/screen-iphone-568h-2x.png" width="640" height="1136"/>
    <splash src="res/screens/ios/screen-iphone-portrait-667h.png" width="750" height="1334"/>
    <splash src="res/screens/ios/screen-iphone-portrait-736h.png" width="1242" height="2208"/>
    <splash src="res/screens/ios/screen-iphone-landscape-736h.png" width="2208" height="1242"/>    
    <splash src="res/screens/ios/screen-ipad-portrait.png" width="768" height="1024"/>
    <splash src="res/screens/ios/screen-ipad-landscape.png" width="1024" height="768"/>
    <splash src="res/screens/ios/screen-ipad-portrait-2x.png" width="1536" height="2048"/>
    <splash src="res/screens/ios/screen-ipad-landscape-2x.png" width="2048" height="1536"/>    
  </platform>  
  
</widget>

Note: the Visual Studio names for the splash screens were used here, not the Cordova ones.

With this change, when you build, the right splash files are generated for the application.

XCode project with correct splash screen images.
XCode project with correct splash screen images.

This leads us to the first question: why are the file names different in Visual Studio and XCode and how are they mapped correctly? If you set the MSBuild output to Diagnostic, you’ll see something like this in the output:

1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-ipad-landscape-2x.png to res\screens\ios\screen-ipad-landscape-2x.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-ipad-landscape.png to res\screens\ios\screen-ipad-landscape.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-ipad-portrait-2x.png to res\screens\ios\screen-ipad-portrait-2x.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-ipad-portrait.png to res\screens\ios\screen-ipad-portrait.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-iphone-568h-2x.png to res\screens\ios\screen-iphone-568h-2x.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-iphone-landscape-736h.png to res\screens\ios\screen-iphone-landscape-736h.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-iphone-portrait-2x.png to res\screens\ios\screen-iphone-portrait-2x.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-iphone-portrait-667h.png to res\screens\ios\screen-iphone-portrait-667h.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-iphone-portrait-736h.png to res\screens\ios\screen-iphone-portrait-736h.png (TaskId:36)
1> —— Copied […]\BlankCordovaApp1\res\screens\ios\screen-iphone-portrait.png to res\screens\ios\screen-iphone-portrait.png (TaskId:36)

and

1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-iphone-portrait.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default~iphone.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-iphone-portrait-2x.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default@2x~iphone.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-ipad-portrait.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-Portrait~ipad.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-ipad-portrait-2x.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-Portrait@2x~ipad.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-ipad-landscape.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-Landscape~ipad.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-ipad-landscape-2x.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-Landscape@2x~ipad.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-iphone-568h-2x.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-568h@2x~iphone.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-iphone-portrait-667h.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-667h.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-iphone-portrait-736h.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-736h.png (TaskId:36)
1> Copying splash from /Users/theuser/remote-builds/builds/16782/cordovaApp/res/screens/ios/screen-iphone-landscape-736h.png to /Users/theuser/remote-builds/builds/16782/cordovaApp/platforms/ios/BlankCordovaApp1/Resources/splash/Default-Landscape-736h.png (TaskId:36)

Here is a table of the iOS splash screens with the mappings between Visual Studio files and Cordova files.

Resolution Device Model VS file Cordova file
320×480 iPhone 1st Gen, 3G & 3GS screen-iphone-portrait.png Default~iphone.png
640×960 iPhone 4 & 4S screen-iphone-portrait-2x.png Default@2x~iphone.png
640×1136 iPhone 5, 5C & 5S screen-iphone-568h-2x.png Default-568h@2x~iphone.png
750×1334 iPhone 6 screen-iphone-portrait-667h.png Default-667h@2x~iphone.png
1242×2208
2208×1242
iPhone 6 Plus screen-iphone-portrait-736h.png
screen-iphone-landscape-736h.png
Default-736h@3x~iphone.png
Default-Landscape-736h.png
768×1024
1024×768
iPad 1, 2 & Mini screen-ipad-portrait.png
screen-ipad-landscape.png
Default-Portrait~ipad.png
Default-Landscape~ipad.png
1536×2048
2048×1536
iPad Air & Mini Retina screen-ipad-portrait-2x.png
screen-ipad-landscape-2x.png
Default-Portrait@2x~ipad.png
Default-Landscape@2x~ipad.png

Note: Though the actual iPhone 6 devices are down-sampled to a resolution of 1080×1920, the splash images must have the size of 1536×2048 and 2048×1536 pixels.

Note: Also take notice that for iPhone 6 Plus, unlike all the other iPhone models, you must provide both portrait and landscape splash screens (just like with iPads).

But where are the mappings handled? In the cordova-lib module there is a file called ios_parser.js where you can find the following code:

    // Update splashscreens
    var splashScreens = config.getSplashScreens('ios');
    var platformSplashScreens = [
        {dest: 'Resources/splash/Default~iphone.png', width: 320, height: 480},
        {dest: 'Resources/splash/Default@2x~iphone.png', width: 640, height: 960},
        {dest: 'Resources/splash/Default-Portrait~ipad.png', width: 768, height: 1024},
        {dest: 'Resources/splash/Default-Portrait@2x~ipad.png', width: 1536, height: 2048},
        {dest: 'Resources/splash/Default-Landscape~ipad.png', width: 1024, height: 768},
        {dest: 'Resources/splash/Default-Landscape@2x~ipad.png', width: 2048, height: 1536},
        {dest: 'Resources/splash/Default-568h@2x~iphone.png', width: 640, height: 1136},
        {dest: 'Resources/splash/Default-667h.png', width: 750, height: 1334},
        {dest: 'Resources/splash/Default-736h.png', width: 1242, height: 2208},
        {dest: 'Resources/splash/Default-Landscape-736h.png', width: 2208, height: 1242}
    ];

    platformSplashScreens.forEach(function(item) {
        var splash = splashScreens.getBySize(item.width, item.height);
        if (splash){
            var src = path.join(appRoot, splash.src),
                dest = path.join(platformRoot, item.dest);
            events.emit('verbose', 'Copying splash from ' + src + ' to ' + dest);
            shell.cp('-f', src, dest);
        }
    });

This code shows an array of (source) splash screens as defined in the config.xml file and an array of pre-defined target splash screen files and their expected resolution. The splash screens in these arrays are mapped by their size (width and height) and source files are copied into destination files. This means that the actual source file names are not important, and in fact you can name your files in any way you want, with the right size they will be correctly mapped.

Note: Be aware of a subtle issue: just copying content from the Cordova documentation for images and splashes to your config.xml file and changing file names won’t work. The path in the Cordova docs looks like res/screen/ios/ while the Visual Studio project path is res/screens/ios/. The mismatch of screen and screens can lead headaches trying to figure out why splash screen images are not correctly setup.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.