NativeScript Core

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>

Improve this document

Demo Source


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 the tns-core-modules/ui/frame module. The method returns the last navigated Frame instance or in case you are in a TabView, the currently selected tab item's Frame instance. For more complex cases or more control, you should use methods like getFrameById or the frame property of Page 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 the tns-core-modules/ui/frame module. This method allows you to get a reference to a Frame 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 of Page instance. Each Page instance carries information about the frame object which navigated to it in the frame property. This lets you navigate with the frame 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 a Frame within a modal page that is not yet opened) can not be retrieved.

Improve this document

Demo Source


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

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");

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.

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);

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);
        });
    }
}

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();

Improve this document

Demo Source


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;
    }
});

Improve this document

Demo Source


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 landing Page instance.
  • Using the navigatedTo event and its arguments of type NavigatedData.
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;
}

Improve this document

Demo Source


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 default actionBarVisibility value and allows to specify the ActionBar visibility for each Page individually.

  • never - this value hides the ActionBar for the current Frame.

  • always - this one specifies that the ActionBar component should always be displayed for the current Frame. If a Page doesn't have a declared ActionBar, 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 the ActionBar immediately in the currently loaded page, you need to set Page's actionBarHidden property to true or false.

<Frame id="my-frame-id" actionBarVisibility="never" defaultPage="home/home-page"/>

Improve this document

Demo Source


See Also Page See Also ActionBar