While there are plenty of ways to implement a splash form in a VCL application, most of those won’t work anymore when they are going to be ported to FMX – especially when it comes to mobile targets. Thanks to one small improvement of FMX over VCL there is a simple and reliable way to implement a splash form in FireMonkey.

In contrast to its VCL sibling the FMX Application.MainForm property is actually writable, allowing to change the applications main form during program run. This opens a complete new approach for our splash form problem with some nice benefits that can be leveraged to make our programming tasks a lot easier.

So, what are the usual reasons for a splash screen? The obvious one is to give the user some visual feedback that the application is actually starting just to avoid him clicking the icon again and again. This leads to the first requirement: The splash screen should best be displayed immediately before doing any lengthy initialization tasks. This is especially valid in mobile environments where applications may easier be declared frozen when they don’t react to the operating system in a timely manner. Thus you should follow these guidelines:

Don’t call lengthy code

inside the unit initialization section!

in the project source!

in the OnCreate, OnShow, OnResize, OnPaint or OnActivate event of the splash form!

A corollary of that is: The splash form should be rendered fast! Most devices can achieve this when they only have to display an image without collecting and displaying a plethora of information from anywhere (f.i. system or database) first. These kinds of information can be updated later – after the splash form is already shown.

My approach here is to have a splash form containing a single TImage. The form is selected as the only form to be created in the forms project options. This directly results into this splash form becoming the main form of the application.

Now we can add our real main form which might actually need some time in its OnCreate event. Remember: Don’t auto create this form! That would happen before the splash form is shown and would somehow counterfeit the reason why we have a splash form in the first place.

As we want to create that form inside our splash form implementation, we add this new unit to the splash forms uses clause (implementation will do).

The difficulty now is to find the right moment to create and show this form. Backed through some experiments, rejecting a couple of possible events that initially came to mind, I ended up with the following approach: start a short timer when the image is displayed and handle the form creation inside the timer event. With a little bit of housekeeping to avoid multiple calls to the relevant code the implementation looks like this:

procedure TFormSplash.FormCreate(Sender: TObject); begin StartupTimer.Enabled := false; StartupTimer.Interval := 500; // can be changed to improve startup speed in later releases end; procedure TFormSplash.SplashImagePaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF); begin StartupTimer.Enabled := not FInitialized; end; procedure TFormSplash.StartupTimerTimer(Sender: TObject); begin StartupTimer.Enabled := false; if not FInitialized then begin FInitialized := true; LoadMainForm; end; end; procedure TFormSplash.LoadMainForm; var form: TForm; begin form := TMainForm.Create(Application); form.Show; Application.MainForm := form; Close; end; 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 procedure TFormSplash . FormCreate ( Sender : TObject ) ; begin StartupTimer . Enabled : = false ; StartupTimer . Interval : = 500 ; // can be changed to improve startup speed in later releases end ; procedure TFormSplash . SplashImagePaint ( Sender : TObject ; Canvas : TCanvas ; const ARect : TRectF ) ; begin StartupTimer . Enabled : = not FInitialized ; end ; procedure TFormSplash . StartupTimerTimer ( Sender : TObject ) ; begin StartupTimer . Enabled : = false ; if not FInitialized then begin FInitialized : = true ; LoadMainForm ; end ; end ; procedure TFormSplash . LoadMainForm ; var form : TForm ; begin form : = TMainForm . Create ( Application ) ; form . Show ; Application . MainForm : = form ; Close ; end ;

That’s it! Really! But hold on – we can do more.

What about having different main forms for different situations. With this approach we can decide which form we create and use as the main form. (Actually this can be done with VCL as well – just differently.)

Although the layout capabilities of FireMonkey are way better than those known from VCL, it may sometimes be desirable to have different form classes used on different devices. Device specific form inheritance used by the FMX form designer might not cover everyone’s needs, so here we are going to use separate main form classes for each supported device.

Assuming we have already created different form classes for say desktop, phone and tablet devices, actually getting the current device class and selecting the appropriate form class is pretty easy using TDeviceInfo from System.Devices. With some small changes our new LoadMainForm method now looks like this:

resourcestring SNotSuitableForDevice = 'The application is not suitable for this device!'; procedure TFormSplash.LoadMainForm; type TFormClass = class of TForm; var form: TForm; formClass: TFormClass; begin formClass := nil; case TDeviceInfo.ThisDevice.DeviceClass of TDeviceInfo.TDeviceClass.Desktop: formClass := TMainFormDesktop; TDeviceInfo.TDeviceClass.Phone: formClass := TMainFormPhone; TDeviceInfo.TDeviceClass.Tablet: formClass := TMainFormTablet; end; if formClass <> nil then begin form := formClass.Create(Application); form.Show; Application.MainForm := form; end else begin ShowMessage(SNotSuitableForDevice); end; Close; end; 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 resourcestring SNotSuitableForDevice = 'The application is not suitable for this device!' ; procedure TFormSplash . LoadMainForm ; type TFormClass = class of TForm ; var form : TForm ; formClass : TFormClass ; begin formClass : = nil ; case TDeviceInfo . ThisDevice . DeviceClass of TDeviceInfo . TDeviceClass . Desktop : formClass : = TMainFormDesktop ; TDeviceInfo . TDeviceClass . Phone : formClass : = TMainFormPhone ; TDeviceInfo . TDeviceClass . Tablet : formClass : = TMainFormTablet ; end ; if formClass < > nil then begin form : = formClass . Create ( Application ) ; form . Show ; Application . MainForm : = form ; end else begin ShowMessage ( SNotSuitableForDevice ) ; end ; Close ; end ;

There may be other ways to achieve the same results. It is also possible that this approach starts to fail miserably on future targets, operating systems or just the next Android or iOS release – we simply don’t know. For the moment it works and it works reliably – at least in the situations I were able to test.

For convenience the source code of this sample application can be downloaded here: SplashFormFMX