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
topmostmethod from thetns-core-modules/ui/framemodule. The method returns the last navigatedFrameinstance or in case you are in aTabView, the currently selected tab item'sFrameinstance. For more complex cases or more control, you should use methods likegetFrameByIdor theframeproperty ofPageclass.
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
getFrameByIdmethod from thetns-core-modules/ui/framemodule. This method allows you to get a reference to aFrameby 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
frameproperty ofPageinstance. EachPageinstance carries information about the frame object which navigated to it in theframeproperty. This lets you navigate with theframeproperty 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
Frameonly for a frame that has been already loaded in the visual tree. Frames that are not still loaded (for example aFramewithin 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
application 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 NavigationEntry 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
AnimationCurveenumeration. 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
Transitionclass. -
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-declarationspackage 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 verify 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
contextproperty
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
bindingContextproperty.
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
navigationContextproperty of your landingPageinstance. -
Using the
navigatedToevent 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 defaultactionBarVisibilityvalue and allows to specify theActionBarvisibility for eachPageindividually. -
never- this value hides theActionBarfor the currentFrame. -
always- this one specifies that theActionBarcomponent should always be displayed for the currentFrame. If aPagedoesn'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 theActionBarimmediately in the currently loaded page, you need to setPage'sactionBarHiddenproperty totrueorfalse.
<Frame id="my-frame-id" actionBarVisibility="never" defaultPage="home/home-page"/>