Modal Dialogs

Create Modal Dialogs in Cross Platform Swift for Android and iOS

June 2021 - Requires SCADE 2.0 or higher

Introduction

A modal dialog is used to get the user's attention and interrupts the user's regular app flow. While the modal dialog is displayed, the user can only perform the actions available in the modal dialog. Only after the user leaves the modal dialog, she can continue the regular app user flow.

In SCADE, we have the following options of displaying modal dialogs

  • Custom Modal Dialogs
  • iOS AlertController
  • Android AlertDialog
  • Any other new iOS and Android option not listed here at the moment...

We will discuss all these options in the following chapters. The UgModalDemo sample app contains all the code.

Example Download

👍

UgModalDialogDemo

Please find the sample project here. Edit the start.swift to run different examples.
Modal Dialog DemoApp

Custom Modal dialog

The SCADE custom modal dialog is the most powerful option, as you can display any kind of UI as a model dialog. So its totally up to you how the model dialog looks, behaves and is displayed. You can use any kind of native control or visual element to create the dialog (textboxes, text, images....).

📘

Short Recap - Pages and Windows

In SCADE, each SCADE page is displayed in a SCADE window. Most of the time, we use one main window and use this to display multiple pages. However, SCADE doesn't stop you from using multiple windows

In our case, we use a second window to display the modal dialog. The modal dialog itself is just our well known SCADE page that can contain any kind of content.

General steps

  1. We design two pages - the parent/main page and the ModalContentPagepage that is used for the modal dialog
  2. We display the main page
  3. Within the main page, we load the ModalContentPage page
  4. We create a new window - modalWindow
  5. We display the ModalContentPage in the new window using the show method
  6. The modal window will receive all the user input, no events will reach the main window
  7. We close the modal window when done and return the results to the main page.

Example Code
The example code is a little bit different from the general steps, as we try to keep all the modal dialog related code in the ModalContentPage to keep the main page class clean

Create Modal Content Page

You design the UI as you like. Make sure that the page's background color is NOT set, therefore providing the modal look.

Create Modal Content Page Class

We use the onExit property to store a closure that gets executed when returning from the modal dialog. The code in the closure processes the results and species what happens when we return from the modal dialog.

We create a window instance modalWindow that holds the window we display on top of the main page window.

When you leave the modalWindow , you first call the close() method of the modalWindow (SCDLatticeWindow), and the modal dialog disappears. We then call onExit to pass control back to the main page.

The show method has been overwritten so that it references the private modalWindow property.

class ModalContentPagePageAdapter: SCDLatticePageAdapter {
    
    // property that holds the closure that is called when leaving the modal window
    var onExit : ((String) -> ()) = { _ in }
    
    // define private window instance to display the modal dialog
    private let modalWindow = SCDLatticeWindow()
    
    // page adapter initialization
    override func load(_ path: String) {
        super.load(path)
        
        // link buttons' click events to code that returns to main page
        for bname in ["btnLeave","btnConfirm"] {
            if let button = self.page!.getWidgetByName(bname) as? SCDWidgetsButton {
                button.onClick.append(SCDWidgetsEventHandler {    _ in
                    
                    // close the window - modal dialog disappears
                    self.modalWindow.close();
                    
                    // return to parent page with result
                    self.onExit("pressed \(bname)")
                })
            }
        }
        
    }
    
    func show() {
        // <page>.show(window) is the default method to show a page in a window
        self.show(self.modalWindow)
}

Create Main Page

In the main page, we specified the closure that is executed when the user returns from the modal dialog. The closure is stored in a property onExit of the ModalContentPage page.

We then show the ModalContentPage when the main page's button is clicked.

The show method of the modalContentPage has been overloaded to use a window instance that is created within the modalContentPage class

import ScadeKit

class MainPageAdapter: SCDLatticePageAdapter {
    
    // create instance of the modal content page
    let modalContentPage = ModalContentPagePageAdapter()
    
    // page adapter initialization
    override func load(_ path: String) {
        super.load(path)
        
        // Load the page (one time in the load method of the main page)
        modalContentPage.load("ModalContentPage.page")
        
        // Specify behaviour when leaving the modal dialog
        modalContentPage.onExit = { print("left modalDialog \($0)") }
        
        // Display custom modal content page when button on main page is clicked
        if let btnModalDialog = self.page!.getWidgetByName("btnCustomModalDialog") as? SCDWidgetsButton {
            btnModalDialog.onClick.append( SCDWidgetsEventHandler{
                
                // the show method is custom method that uses
                // the window instantiated within the modalContentPage class
                _ in self.modalContentPage.show()
            })}
    }
    
}

iOS UIAlertController

Displaying the UIAlertController couldn't be easier. Just use the Swift code you are used to. In this example, we use .alert type

#if os(iOS)
	import UIKit
#endif

... 

func showUIAlertController() {
    // A example of how to display the UIAlertController on iOS
    // This is the same Swift code you would write on XCode
    // It works only on iOS, therefore the #if #endif command for conditional compilation
    // 
    // Important. Use SCDApplication.rootViewController to access the UIViewController instance
    
    #if os(iOS)
    
    let alert = UIAlertController(title: "Did you bring your towel?",
                                  message: "It's recommended you bring your towel before continuing.",
                                  preferredStyle: .alert)
    
    alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { _ in print("yes") }))
    
    alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { _ in print("no") }))
    
    SCDApplication.rootViewController?.present(alert, animated: true)
    
    #endif
}

There are two things that are slightly different

  • enclose the code in #if #endif conditional compilation commands, as this code doesn't compile on Android
  • Use the rootViewController property to get a handle to the RootViewController and display the dialog

Android AlertDialog

Coming soon.