Recently, Erin Rahnenfuehrer brought to you an awesome CFA (Cascades Field Agency) compilation of the BlackBerry 10 QML Performance Tips. Point five in this article referred to the term “delayed loading”, which allows you to minimize the application startup time by deferring the loading of UI components that are not part of the startup screen. This concept is particularly important if you are using a tabbed pane with multiple tabs as your application’s main navigation structure. Can you imagine you could be cutting down the loading time of your n-tab tabbed pane by a factor of n, if you apply this technique!?
Up until the 10.1 BlackBerry SDK, “delayed loading” meant either to use ComponentDefinition or ControlDelegate for dynamic loading of QML components. While using the ControlDelegate API is declarative, it can only be used with components that inherit from the Control class and hence cannot be used for deferred loading of Tabs in a TabbedPane. The ComponentDefintion API, on the other hand, is imperative and for many of you didn’t quite feel right at home with the declarative nature of the rest of your QML code.

Listening to your feedback, we are now introducing in the new 10.2 SDK a new API called Delegate and two new Tab properties: delegate and delegateActivationPolicy that together harmoniously allow you to manage the loading of tabs in the same QML declarative manner you are used to.

In the case of synchronous loading, the Page is included directly under the Tab as follows:

TabbedPane {
Tab {
Id: tab1
title: "tab1"
Page {}
}
}

For loading tabs asynchronously, all you need to do differently is to include the Page as the source of the Delegate class, which is set as the delegate property in the tab. Here is how to do so:

The Delegate class provides object lifecycle management similar to ControlDelegate using the active flag. In the above code, the TabDelegateActivationPolicy.Default toggles the active flag of the Delegate with selection, i.e. it sets it to true when the Tab is selected and false if otherwise. For other enumeration for specifying when a tab should set the active property of its delegate, check the API documentation for the TabDelegateActivationPolicy.

Hope you make use of this new built-in API for improved performance of your Cascades app… @SamarAbdelsayed

Many applications require the Menu UI component. This is the global menu within the app which is visible when the user swipes down on the top edge of the screen. It is the place to keep global ActionItem objects like Info, Settings or Help. However, it is easy to get confused on how to actually display these resulting pages and which components to use for them; namely, between Sheet & NavigationPane. Hence, we decided to provide you a sample app on how to actually integrate and tie all these components together, especially if you had a TabbedPane with NavigationPane on the Menu.

Key differences between Sheet & NavigationPane

Before we start exploring the different options with these two UI components, we should recap the general Cascades UI guidelines behind them (respective sections are hyperlinked inline).

Sheet – This should be used whenever there is a break in the user’s flow. That is, anything that takes the user away from their current action(s) and navigation flow; the Sheet slides in from the bottom. Typically, a sheet is presented for things such as sending emails, taking user credentials and of course, for application menu items such as Info, Help or Settings! This is typically the easiest way to implement action items on the application menu.

NavigationPane – This should be used whenever there is any drill down navigation involved. You get the nice sliding transition (from the right) for free, along with the swipe gestures for navigating (peek!). In terms of the application menu, you might want to use NavigationPane instead of Sheet if you want to maintain the stack like navigation for your app. Sometimes, you might also want it if you have additional drill down navigation lists within your settings page (note that you can also have a navigation pane within a sheet). However, if you are in fact pushing navigation page objects through the application menu, especially with a TabbedPane, there are certain edge cases and work around that you would have to watch out for (mentioned at the end).

For the purpose of this blog post and the github sample code provided, we are showing the implementation of both the components. However, only one of these approaches should be chosen for the application menu and it should be a consistent behavior.

Related BlackBerry 10 Tips:

If you swipe back by touching the navigation pane’s back arrow, you can pop back all the pages in your navigation stack (as opposed to having to press back multiple times!).

The primary contents or pages of the app should not be presented through the application Menu; this area is mainly designed for items which are rarely used but accessible from anywhere in the app.

Show me some code already!

Enabling the menu is the first thing you would have to worry about. If using the navigation pane, right before we are pushing the settings page, we have to disable the application menu (to be enabled back when that page is popped). This is to ensure that there is no way to push the same settings page on top of itself.

And how are these infoSheet and settingsPage objects created? They can be included as attachedObjects. To show some code encapsulation, we used separate QML files here for both. They can be defined inline as well, if the respective code is within them is too trivial (but why cluster the code, right?)

If you were using a sheet, this would have been enough. But if you pushed a Page using NavigationPane (like we did for this Settings page), you would have to enable the menu back on again (remember, we disabled it before we pushed it on).

Now, to finally address some of the concerns if you are using NavigationPane from the Menu. It should be noted that Cascades doesn’t allow adding NavigationPane directly to the global TabbedPane or to a Page object (Page can be added to a NavigationPane, but not the other way around).These limitations have been imposed by Cascades intentionally, to enforce certain UI behaviours. But we do need a NavigationPane object to push a Page onto. Hence, in this case, you are left with having to add a NavigationPane object for each and every Tab that we are showing. We also have to keep track of the handle of the currently-active Tab, just so that we know which NavigationPane object we should be pushing the new Page onto.

For your coding pleasure

Phew! Yes, that may have seemed like a long blog post but it is actually much simpler when you look at the complete source files. Feel free to check out the actual code project on github right here. Compile, deploy and get going with the menus of those awesome Cascades apps of yours!