Frame
The Frame
class represents the logical unit that is responsible for navigation between different pages. An application can have single or multiple Frame
instances depending on the business logic and requirements.
Frame Creation
The frame should have the defaultPage
property set (mandatory). The page passed as value for defaultPage
will load on frame initialization.
The id
property can be used to get a reference to the frame instance.
<Frame id="root-frame" defaultPage="main-page"/>
More complex application structure can be created by using multiple frame instances.For example, you can create a TabView
while using different frames for each tab item.
<TabView>
<TabViewItem title="First">
<Frame id="firstFrame" defaultPage="home/home-page" />
</TabViewItem>
<TabViewItem title="Second">
<Frame id="secondFrame" defaultPage="second/second-page" />
</TabViewItem>
</TabView>
Frame Reference
The navigation in NativeScript is based on the Frame
API and using navigate
method of the wanted frame.
To get a reference to the Frame
instance you need use the following methods or properties:
- the
topmost
method from thetns-core-modules/ui/frame
module. The method returns the last navigatedFrame
instance or in case you are in aTabView
, the currently selected tab item'sFrame
instance. For more complex cases or more control, you should use methods likegetFrameById
or theframe
property ofPage
class.
const Frame = require("tns-core-modules/ui/frame").Frame;
const topmostFrame = Frame.topmost();
import { Frame } from "tns-core-modules/ui/frame";
const topmostFrame: Frame = Frame.topmost();
- the
getFrameById
method from thetns-core-modules/ui/frame
module. This method allows you to get a reference to aFrame
by a id that you specified on the element. Note that this searches for already navigated frames and won't find frames that are not yet displayed like in a modal view for example.
<Frame id="root-frame" defaultPage="main-page"/>
const getFrameById = require("tns-core-modules/ui/frame").getFrameById;
const currentFrame = getFrameById("root-frame");
import { getFrameById } from "tns-core-modules/ui/frame";
const currentFrame: Frame = getFrameById("root-frame");
- the
frame
property ofPage
instance. EachPage
instance carries information about the frame object which navigated to it in theframe
property. This lets you navigate with theframe
property as well.
function onTap(args) {
const button = args.object;
const page = button.page;
const myFrame = page.frame;
myFrame.navigate("secondary-page");
}
exports.onTap = onTap;
import { Button } from "tns-core-modules/ui/button";
import { Page } from "tns-core-modules/ui/page";
export function onTap(args) {
const button: Button = args.object;
const page: Page = button.page;
const myFrame: Frame = page.frame;
myFrame.navigate("secondary-page");
}
Note We can get a reference to a
Frame
only for a frame that has been already loaded in the visual tree. Frames that are not still loaded (for example aFrame
within a modal page that is not yet opened) can not be retrieved.
Basic Navigation
Default page
To load a default (initial) page in your application use the defaultPage
property of the Frame
element.
With the example below the applicaiton will load a page located in <project-folder>/app/home/first-page.xml
<Frame id="my-frame-id" defaultPage="home/first-page"/>
Navigate by page name
To navigate between pages, you can use the navigate
method of the desired Frame
instance.
const getFrameById = require("tns-core-modules/ui/frame").getFrameById;
// Example using `getFrameById(frameId)` to get a `Frame` reference
// As an alternative, we could use `topmost()` method or `page.frame` property
const frame = getFrameById("my-frame-id");
frame.navigate("home/second-page");
import { getFrameById } from "tns-core-modules/ui/frame";
// Example using `getFrameById(frameId)` to get a `Frame` reference
// As an alternative, we could use `topmost()` method or `page.frame` property
const frame = getFrameById("my-frame-id");
frame.navigate("home/second-page");
Navigate by NavigationEntry object
The navigate
method accepst NavigationEntry
object.
const frame = getFrameById("my-frame-id");
const navigationEntry = {
moduleName: "home/second-page",
context: { info: "something you want to pass to your page" },
animated: false
};
frame.navigate(navigationEntry);
const frame = getFrameById("my-frame-id");
// import { NavigationEntry } from "tns-core-modules/ui/frame";
const navigationEntry: NavigationEntry = {
moduleName: "home/second-page",
context: { info: "something you want to pass to your page" },
animated: false
};
frame.navigate(navigationEntry);
Full list of the NavigaitonEntry properties. Note that all of them are optional. Even the moduleName
is optional, as alternatively you can pass a dynamically created page via create
.
- animated - True to navigate to the new Page using animated transitions, false otherwise.
- backstackVisible - True to record the navigation in the backstack, false otherwise. If the parameter is set to false then the Page will be displayed but once navigated from it will not be able to be navigated back to.
- bindingContext - An object to become the binding context of the page navigating to. Optional.
- clearHistory - True to clear the navigation history, false otherwise. Very useful when navigating away from login pages.
- context - An object passed to the onNavigatedTo callback of the Page. Typically this is used to pass some data among pages. Optional.
- create - A function used to create the View instance. Optional.
- moduleName - The name of the module containing the View instance to load. Optional.
- transition - Specifies an optional navigation transition for all platforms. If not specified, the default platform transition will be used.
- transitionAndroid - Specifies an optional navigation transition for Android. If not specified, the default platform transition will be used.
- transitioniOS - Specifies an optional navigation transition for iOS. If not specified, the default platform transition will be used.
Navigate without history
You can navigate to a page without adding this navigation to the history. Set the backstackVisible
property of the NavigationEntry
to false
.
When the property is set to false, then the Page will be displayed, but once navigated from it will not be able to be navigated back to.
const frame = getFrameById("my-frame-id");
const navigationEntry = {
moduleName: "home/second-page",
backstackVisible: false
};
frame.navigate(navigationEntry);
const frame = getFrameById("my-frame-id");
// import { NavigationEntry } from "tns-core-modules/ui/frame";
const navigationEntry: NavigationEntry = {
moduleName: "home/second-page",
backstackVisible: false
};
frame.navigate(navigationEntry);
Clear history
You can navigate to a new page and decide to completely clear the entire navigation history. Set the clearHistory
property of the NavigationEntry
to true
.
This will prevent the user from going back to pages previously visited. This is extremely useful if you have a multiple-page authentication process and you want to clear the authentication pages once the user is successfully logged in and redirected to the start page of the application.
const frame = getFrameById("my-frame-id");
const navigationEntry = {
moduleName: "home/second-page",
clearHistory: true
};
frame.navigate(navigationEntry);
const frame = getFrameById("my-frame-id");
// import { NavigationEntry } from "tns-core-modules/ui/frame";
const navigationEntry: NavigationEntry = {
moduleName: "home/second-page",
clearHistory: true
};
frame.navigate(navigationEntry);
Navigation transitions
Transition properties
By default, all navigation will be animated and will use the default transition for the respective platform (UINavigationController
transitions for iOS and Fragment
transitions for Android). To change the transition type, set the navigationTransition
property of the NavigationEntry
to an object conforming to the NavigationTransition
interface. The NavigationTransition
interface has four optional properties:
-
curve - An optional transition animation curve. Possible values are contained in the
AnimationCurve
enumeration. Alternatively, you can pass an instance of type UIViewAnimationCurve for iOS or android.animation.TimeInterpolator for Android. - duration - The length of the transition in milliseconds. If you do not specify this, the default platform transition duration will be used.
-
instance - An user-defined instance of the
Transition
class. -
name - Can be one of the built-in transitions:
- curl (same as curlUp) (iOS only)
- curlUp (iOS only)
- curlDown (iOS only)
- explode (Android Lollipop(21) and up only)
- fade
- flip (same as flipRight)
- flipRight
- flipLeft
- slide (same as slideLeft)
- slideLeft
- slideRight
- slideTop
- slideBottom
const frame = getFrameById("my-frame-id");
const navigationEntry = {
moduleName: "home/second-page",
animated: true,
// Set up a transition property on page navigation.
transition: {
name: "slide",
duration: 380,
curve: "easeIn"
}
};
frame.navigate(navigationEntry);
const frame = getFrameById("my-frame-id");
// import { NavigationEntry } from "tns-core-modules/ui/frame";
// import { AnimationCurve } from "tns-core-modules/ui/enums";
const navigationEntry: NavigationEntry = {
moduleName: "home/second-page",
animated: true,
// Set up a transition property on page navigation.
transition: {
name: "slide",
duration: 380,
curve: AnimationCurve.easeIn
}
};
frame.navigate(navigationEntry);
Default transition for specific Frame
To specify a default transition for all frame navigations, set the transition property of the frame you are navigating with.
// const getFrameById = require("tns-core-modules/ui/frame").getFrameById;
// const myFrame = getFrameById("firstFrame");
myFrame.transition = { name: "flip" };
myFrame.navigate("main-page");
// const getFrameById = require("tns-core-modules/ui/frame").getFrameById;
// const frame = getFrameById("firstFrame");
frame.transition = { name: "flip" };
frame.navigate("main-page");
Default transition for the application
To specify a default transition for all navigations across the entire app, set the static defaultTransition
property of the Frame
class.
// const frameModule = require("tns-core-modules/ui/frame");
frameModule.Frame.defaultTransition = { name: "fade" };
// import * as frameModule from "tns-core-modules/ui/frame";
frameModule.Frame.defaultTransition = { name: "fade" };
Platform-specific transitions
To specify different transitions for the different platforms use the transitioniOS
and transitionAndroid
properties of the NavigationEntry
.
const navigationEntry = {
moduleName: "main-page",
animated: true,
// Set up platform specific transitions.
transitioniOS: {
name: "curl",
duration: 380,
curve: "easeIn"
},
transitionAndroid: {
name: "explode",
duration: 300,
curve: "easeOut"
}
};
const frame = getFrameById("my-frame");
frame.navigate(navigationEntry);
const navigationEntry: NavigationEntry = {
moduleName: "main-page",
animated: true,
// Set up platform specific transitions.
transitioniOS: {
name: "curl",
duration: 380,
curve: AnimationCurve.easeInOut
},
transitionAndroid: {
name: "explode",
duration: 300,
curve: AnimationCurve.spring
}
};
const frame = getFrameById("my-frame");
frame.navigate(navigationEntry);
Custom Transitions
Instead of setting the name property to one of the predefined transitions, you can set the instance property of the NavigationTransition
to an instance of a class that inherits from Transition
.
You can create your custom user-defined transition by writing platform-specific code to animate the transition.
To do that you need to inherit from the Transition
class and override one method for each platform.
Since there will be platform-specific code, you need to separate your code into two separate files.
Here is an example of a custom transition that shrinks the disappearing page while expanding the appearing page by using a scale affine transform.
NOTE: The following example uses native APIs. When using TypeScript, you need to add a dev dependency to the
tns-platform-declarations
package to use these native APIs without compiler errors. For more information, see the Intellisense and access to native APIs via TypeScript section.
custom-transition.android.js/ts
const transition = require("tns-core-modules/ui/transition");
const floatType = java.lang.Float.class.getField("TYPE").get(null);
const CustomTransition = (function (_super) {
__extends(CustomTransition, _super);
function CustomTransition() {
_super.apply(this, arguments);
}
CustomTransition.prototype.createAndroidAnimator = function(transitionType) {
const scaleValues = java.lang.reflect.Array.newInstance(floatType, 2);
switch (transitionType) {
case transition.AndroidTransitionType.enter:
case transition.AndroidTransitionType.popEnter:
scaleValues[0] = 0;
scaleValues[1] = 1;
break;
case transition.AndroidTransitionType.exit:
case transition.AndroidTransitionType.popExit:
scaleValues[0] = 1;
scaleValues[1] = 0;
break;
default:
break;
}
const objectAnimators = java.lang.reflect.Array.newInstance(android.animation.Animator.class, 2);
objectAnimators[0] = android.animation.ObjectAnimator.ofFloat(null, "scaleX", scaleValues);
objectAnimators[1] = android.animation.ObjectAnimator.ofFloat(null, "scaleY", scaleValues);
const animatorSet = new android.animation.AnimatorSet();
animatorSet.playTogether(objectAnimators);
const duration = this.getDuration();
if (duration !== undefined) {
animatorSet.setDuration(duration);
}
animatorSet.setInterpolator(this.getCurve());
return animatorSet;
};
return CustomTransition;
})(transition.Transition);
exports.CustomTransition = CustomTransition;
import { Transition, AndroidTransitionType } from "tns-core-modules/ui/transition";
export class CustomTransition extends Transition {
public createAndroidAnimator(transitionType: string): android.animation.Animator {
const scaleValues = (<any>Array).create("float", 2);
switch (transitionType) {
case AndroidTransitionType.enter:
case AndroidTransitionType.popEnter:
scaleValues[0] = 0;
scaleValues[1] = 1;
break;
case AndroidTransitionType.exit:
case AndroidTransitionType.popExit:
scaleValues[0] = 1;
scaleValues[1] = 0;
break;
default:
break;
}
const objectAnimators = (<any>Array).create(android.animation.Animator, 2);
objectAnimators[0] = android.animation.ObjectAnimator.ofFloat(null, "scaleX", scaleValues);
objectAnimators[1] = android.animation.ObjectAnimator.ofFloat(null, "scaleY", scaleValues);
const animatorSet = new android.animation.AnimatorSet();
animatorSet.playTogether(objectAnimators);
const duration = this.getDuration();
if (duration !== undefined) {
animatorSet.setDuration(duration);
}
animatorSet.setInterpolator(this.getCurve());
return animatorSet;
}
}
custom-transition.ios.js/ts
// const transition = require("tns-core-modules/ui/transition");
const CustomTransitionIOS = (function (_super) {
__extends(CustomTransition, _super);
function CustomTransition() {
_super.apply(this, arguments);
}
CustomTransition.prototype.animateIOSTransition = function(containerView, fromView, toView, operation, completion) {
toView.transform = CGAffineTransformMakeScale(0, 0);
fromView.transform = CGAffineTransformIdentity;
switch (operation) {
case UINavigationControllerOperation.UINavigationControllerOperationPush:
containerView.insertSubviewAboveSubview(toView, fromView);
break;
case UINavigationControllerOperation.UINavigationControllerOperationPop:
containerView.insertSubviewBelowSubview(toView, fromView);
break;
default:
break;
}
const duration = this.getDuration();
const curve = this.getCurve();
UIView.animateWithDurationAnimationsCompletion(duration, () => {
UIView.setAnimationCurve(curve);
toView.transform = CGAffineTransformIdentity;
fromView.transform = CGAffineTransformMakeScale(0, 0);
}, completion);
};
return CustomTransitionIOS;
})(transition.Transition);
exports.CustomTransitionIOS = CustomTransitionIOS;
// import { Transition } from "tns-core-modules/ui/transition";
declare let UINavigationControllerOperation: any; // or use tns-platform-declarations
export class CustomTransitionIOS extends Transition {
public animateIOSTransition(containerView: UIView,
fromView: UIView,
toView: UIView,
operation: UINavigationControllerOperation,
completion: (finished: boolean) => void): void {
const originalToViewTransform = toView.transform;
const originalFromViewTransform = fromView.transform;
// http://stackoverflow.com/questions/216076/uiview-scale-to-0-using-cgaffinetransformmakescale
const scaleTransform = CGAffineTransformMakeScale(0.0001, 0.0001);
toView.transform = scaleTransform;
fromView.transform = CGAffineTransformIdentity;
switch (operation) {
case UINavigationControllerOperation.UINavigationControllerOperationPush:
containerView.insertSubviewAboveSubview(toView, fromView);
break;
case UINavigationControllerOperation.UINavigationControllerOperationPop:
containerView.insertSubviewBelowSubview(toView, fromView);
break;
default:
break;
}
const duration = this.getDuration();
const curve = this.getCurve();
UIView.animateWithDurationAnimationsCompletion(duration, () => {
UIView.setAnimationCurve(curve);
toView.transform = CGAffineTransformIdentity;
fromView.transform = scaleTransform;
}, (finished: boolean) => {
toView.transform = originalToViewTransform;
fromView.transform = originalFromViewTransform;
completion(finished);
});
}
}
Navigate Back
Each frame tracks the pages the user has visited in a navigation stack. To go back to a previous page,
you need to use the goBack
method of the current frame instance.
To veify that back navigation is possible, you can use the canGoBack
boolean property.
// const getFrameById = require("tns-core-modules/ui/frame").getFrameById;
const myFrame = getFrameById("my-frame");
myFrame.goBack();
// import { getFrameById } from "tns-core-modules/ui/frame";
const myFrame = getFrameById("my-frame");
myFrame.goBack();
Dynamic Navigation
A more dynamic way of navigating can be done by providing a function that returns the instance of the page to which you want to navigate.
const frame = getFrameById("my-frame-id");
frame.navigate({
create: () => {
const stack = new StackLayout();
const label = new Label();
label.text = "Hello, world!";
stack.addChild(label);
const page = new Page();
page.content = stack;
return page;
}
});
const frame = getFrameById("my-frame-id");
frame.navigate({
create: () => {
const stack = new StackLayout();
const label = new Label();
label.text = "Hello, world!";
stack.addChild(label);
const page = new Page();
page.content = stack;
return page;
}
});
Navigation Context
Passing context during navigation
When you navigate to another page, you can pass context to the page with a NavigationEntry
object. The navigaiton entry provides two different (optional) propertes to work with:
- The
context
property
exports.onNavigate = function(args) {
const button = args.object;
const page = button.page;
const frame = page.frame;
const navEntryWithContext = {
moduleName: "home/second-page",
context: {
name: "John",
age: 25,
isProgramer: true
}
};
frame.navigate(navEntryWithContext);
};
import { EventData } from "tns-core-modules/data/observable";
import { Button } from "tns-core-modules/ui/button";
import { Page } from "tns-core-modules/ui/page";
import { Frame, NavigationEntry } from "tns-core-modules/ui/frame";
export function onNavigate(args: EventData) {
let button = <Button>args.object;
let page = <Page>button.page;
let frame = <Frame>page.frame;
let navEntryWithContext: NavigationEntry = {
moduleName: "home/second-ts-page",
context: {
name: "John",
age: 25,
isProgramer: true
}
};
frame.navigate(navEntryWithContext);
}
- The
bindingContext
property.
const navEntryWithBindingContext = {
moduleName: "home/second-ts-page",
// when using bindingContext the landing page will automatically
// receive and set this object as page.bindingContext with navigatedTo event
bindingContext: {
name: "John",
age: 25,
isProgramer: true
}
};
const navEntryWithBindingContext: NavigationEntry = {
moduleName: "home/second-ts-page",
// when using bindingContext the landing page will automatically
// receive and set this object as page.bindingContext with navigatedTo event
bindingContext: {
name: "John",
age: 25,
isProgramer: true
}
};
Both properties are used to pass context while navigating, but the bindingContext
property will automatically assign the binding context for the landing page.
Retreiving context during navigation
Any context send with bindingContext
is automatically assigned as binding context for the navigated (landing) page.
Retreiving a context send through the context
property, can be achieved with two different approaches.
- Accessing the
navigationContext
property of your landingPage
instance. - Using the
navigatedTo
event and its arguments of typeNavigatedData
.
function onNavigatedTo(args) {
const page = args.object;
const navigationContext = page.navigationContext;
// The navigation event arguments are of type NavigatedData and provide another way to grab the passed context
const context = args.context;
page.bindingContext = navigationContext;
}
exports.onNavigatedTo = onNavigatedTo;
import { Page, NavigatedData } from "tns-core-modules/ui/page";
// Event handler for Page "navigatedTo" event attached in details-page.xml e.g.
export function onNavigatedTo(args: NavigatedData): void {
const page: Page = <Page>args.object;
const navigationContext = page.navigationContext;
// The navigation event arguments are of type NavigatedData and provide another way to grab the passed context
const context = args.context;
page.bindingContext = navigationContext;
}
Action Bar Visibility
Тhе actionBarVisibility
is a property that controls the visibility of the Navigation Bar in iOS and the Action Bar in Android for the current Frame
. It should be set directly to the Frame
and has three option values auto
, never
, always
.
auto
- this is the defaultactionBarVisibility
value and allows to specify theActionBar
visibility for eachPage
individually.never
- this value hides theActionBar
for the currentFrame
.always
- this one specifies that theActionBar
component should always be displayed for the currentFrame
. If aPage
doesn't have a declaredActionBar
, a default one will be displayed.
Note: If you have set up some of the two properties(
never
,always
) and you need to hide/show theActionBar
immediately in the currently loaded page, you need to setPage
'sactionBarHidden
property totrue
orfalse
.
<Frame id="my-frame-id" actionBarVisibility="never" defaultPage="home/home-page"/>