Friday, June 29, 2012

Tutorial 4 - A Splash Screen (Updated 17/01/16)

Webcomic Courtesy of Ethanol & Entropy

4.1 Should you do a Splash Screen?

Personally we like adding a splash screen to our Apps. However before starting todays tutorial we should point out Apple's position on this. Quoting from the iOS Human Interface Guidelines:

"Avoid using your launch image as an opportunity to provide:
  • An “application entry experience,” such as a splash screen
  • An About window
  • Branding elements, unless they are a static part of your application’s first screen
Because users are likely to switch among applications frequently, you should make every effort to cut launch time to a minimum, and you should design a launch image that downplays the experience rather than drawing attention to it."
So for all the reasons that Apple don't like a splash screen, we do. There are some lines with Apple that you can't cross and will get you rejected from the App store. This isn't one of those, We have 8 Apps in iTunes and they all have splash screens. So read up on the subject and make your own decision.
Note that even if you decide to toe the Apple line, you can use this class in less controversial instances (eg a win / lose screen or credits).

4.2 Classes You Need to Splash

Most of the classes that we will add are not strictly required to make a splash but when writing a real world App you will probably have something similar so we have included them. There is not a lot of new code required, it is more a case of stitching together what we have already covered. You will need:
If you have been following along then most of these classes will already be in your Menu project. The code for Fader and RoundBorder are shown in Interludes 3 and 4 below. The two classes we need to cover here are the changes to Main and SplashScreen.

4.3 The Main Class

Okay, let's have a look at the revised Main class first.

-- Use this function to perform your initial setup

function setup()
    print("Menu & Splash Screen Demo")
-- [[ saveProjectInfo("key", "value") allows you to assign metadate to your App. It currently supports the Description and Author keys. The value that you assign to the Description will appear under your App in the Project Browser screen. This block of code is optional but good practise. ]]

    saveProjectInfo("Description", "Menu & Splash Screen Demonstration Code.")
    saveProjectInfo("Author", "Reefwing Software")
    saveProjectInfo("Version", "1.0")
    -- [[ The next block of code is also optional. It will keep an eye on our Frames Per Second (FPS) as the App runs. ]]
    FPS = 0
    timeInterval = 0
    frameCount = 0
    -- [[ create a new button which will represent our App menu, it wont be visible until after the splash screen fades away. ]]
    button = Button("  Easy Levels  ")
    button.action = function() buttonPressed() end
    -- [[ Create the splash screen. The variables passed to the SplashScreen class are text to display and the fading speed of the flash screen. The larger the fading speed the faster it fades. One is the minimum speed. ]]
    splashScreen = SplashScreen("Splash Screen Demo", 1)

-- This function gets called once every frame

function draw()

    -- This sets a black background color 
    background(0, 0, 0)

    -- Do your drawing here
-- [[ This block of code is optional. It calculates and displays every second the Frames Per Second (FPS) rate that our App is drawing at. ]]

    frameCount = frameCount + 1
    timeInterval = timeInterval + DeltaTime
    if timeInterval > 1 then
        FPS = math.round(frameCount / timeInterval)
        timeInterval = 0
        frameCount = 0
    -- [[ Draw Splash Screen and when it has finished fading (splashScreen.shown = true) the menu button(s). ]]
    if splashScreen.shown == true then
        splashScreen: draw()

-- [[ Button related functions - described in earlier tutorials. ]]

function drawButton()
    button.pos = vec2(400, HEIGHT/2)

function buttonPressed()
    print("Button Pressed")

function touched(touch)

-- [[ This function was written by Vega and is used in the FPS calculation. It can be deleted if you are not including the FPS code. ]]

function math.round(num)
    return math.floor(num + 0.5)

4.4 The SplashScreen Class

You can tailor this class to include whatever you want to show on your splash screen. We picked some obvious things like text and a logo. To add a bit more pizzaz we added a rounded border.

SplashScreen = class()

function SplashScreen:init(splashText, fadeSpeed, logoURL)
    -- you can accept and set parameters here

-- [[ Those sharp eyed may notice that we only passed in two parameters from our Main class to this class. Consequently Lua will set logoURL to nil. We did this to demonstrate this point. For the tutorial we have hard coded the logo loaded but we will show you in the code below where you can change this. Hopefully most of the variables below are self explanatory. ]]
    self.splashText = splashText
    self.fadeSpeed = fadeSpeed
    self.logoURL = logoURL
    self.shown = false

-- [[ We created some colour name variables to make the code more readable. Andrew Stacey has produced a mammoth list of colour names, included in the Roller Coaster example App which comes with Codea. The class is appropriately called ColourNames. You could copy this into your projects if you want to use the names that he has defined. It is overkill for our purposes. ]]
    blackColour = color(0, 0, 0)
    redColour = color(255, 0, 46, 255)
    blueColour = color(0, 188, 255, 255)

-- [[ Start by initialising our splash screen border. This class is described in Interlude 4. ]]
    splashBorder = RoundBorder(10, 10, WIDTH - 10, HEIGHT - 10, 1, blueColour, blackColour)
    -- init Fader
    -- the larger the fade speed the quicker the fade
    fader = Fader(fadeSpeed)
    if fader.fading == false then
        fader: fade(fadeAnimationDone)
-- [[ The next bit of code comes from Simeon's Image IO example project which is included with Codea. In particular the parts related to downloading an image and displaying it. In your App you would more likely use a locally stored image so that it would be displayed regardless of whether you have an internet connection. ]]

     -- Start the logo request, didGetLogo is our callback function
    logo = nil
-- [[ The next line of code is from the original example and will download the Two Lives Left logo. In a blatant example of self promotion we have replaced this with the Reefwing Software logo. Feel free to insert your own logo URL here. Note depending on the size of the logo used you may have to tweak where it is drawn. To make this class more general and remove the hard coded logo, you can replace this text with self.logoURL. Just make sure you add this variable in the Main class when you initialise the splash screen. didGetLogo is called when the http request finishes. ]]

    --http.request( "", didGetLogo )
    http.request( "", didGetLogo )

function SplashScreen:draw()
    -- Codea does not automatically call this method
-- [[ Set the background black and define the splash text. We haven't drawn it yet. ]]

    local w,h = textSize(self.splashText)
    -- Detect if the splash screen has started fading.

    if fader.fading == true then
-- [[ Start by drawing the border. The order that you draw is important. Drawing this last would make the text and logo hidden. ]]

        splashBorder: draw()
        -- Draw the logo we downloaded (when it's ready!)
        if logo ~= nil then
            sprite( logo, WIDTH - logo.width/2 - 20, logo.height)
        -- Finally draw the text.

        text(self.splashText, WIDTH/2, HEIGHT/2)
        fader: draw()
        self.shown = true

function fadeAnimationDone()
    -- Call back function for Fader complete
    print("Splash Screen Fade Complete")

-- [[ The next bit of code also comes from Simeon's Image IO example project which is included with Codea. ]]

function didGetLogo( theLogo, status, headers )
    print( "Response Status: " .. status )
    -- Store logo in our global variable
    logo = theLogo
    -- Check if the status is OK (200)
    if status == 200 then
        print( "Downloaded logo OK" )
        print( " Image dimensions: " .. 
                    logo.width .. "x" .. logo.height )
        print( "Error downloading logo" )