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"/>