Nativescript: [Tracking] Bottom navigation & tabs components

Created on 27 Feb 2019  路  30Comments  路  Source: NativeScript/NativeScript

Overview

The TabView component is a navigation component that is usually at the base of a mobile app. As such it's very important to get it right. Following lots of reported issues and discussions, we decided to do a re-design of how the developer uses and customizes the component. This should be done by introducing several new components including BottomNavigation and Tabs and there should be no breaking changes for existing apps. See the design doc below for the detailed information.

Design

Implementation details and motivation of the feature can be found in its design document.

Tasks

NativeScript 6.0 (components released as BETA / EXPERIMENTAL)

**BottomNavigation** - [x] native implementation [Android] - [x] nesting navigations - [x] load one view at a time - [x] TabStrip declaration - [x] Angular support **Tabs** - [x] native implementation [iOS] - [x] nesting navigations - [x] pre-load one view to the sides by default - [x] TabStrip declaration - [x] offscreenTabLimit - [x] tabsPosition - [x] swipeEnabled - [x] Angular support **TabStrip** - [x] CSS background-color **TabStripItem** - [x] CSS pseudo class :active - [x] CSS background-color - [x] CSS color - [x] CSS font - [x] CSS font-size - [x] CSS highlight-color - [x] CSS text-transform - [x] iconSource font-icon support - [x] Image instead of iconSource declaration - [x] Label instead of title declaration Issues: - [x] [iOS Tabs] Tap on tabs does not change item - NativeScript/NativeScript#7435 - [x] [iOS Tabs] Tabs icons does not work - NativeScript/NativeScript#7436 - [x] [iOS Tabs] A crash on swiping a content item - NativeScript/NativeScript#7459 - [x] [BottomNavigation]: crash when navigating without transition animation - NativeScript/NativeScript#7481 - [ ] [BottomNavigation] - crash when used with nativescript-advanced-webview https://github.com/NativeScript/NativeScript/issues/7901 - [ ] [iOS][BottomNavigation] Images are not vertically centered - https://github.com/NativeScript/NativeScript/issues/7991 - [ ] [BottomNavigation] Calling js method onSelectedPositionChange failed - https://github.com/NativeScript/NativeScript/issues/8057

NativeScript 6.1 (components released as OFFICIAL)

**BottomNavigation** - [x] Vue.js support - [x] No TabStrip support (tabBarVisibility/no declaration) - [x] limit bar to 5 tabs **Tabs** - [x] Vue.js support - [x] safe area improvements - [x] No TabStrip support (tabBarVisibility/no declaration) **TabStrip** - [x] itemTap event: https://github.com/NativeScript/NativeScript/pull/7711 **TabStripItem** - [x] tap event: https://github.com/NativeScript/NativeScript/pull/7693 - [x] Label title CSS support - [x] Image icon CSS support Issues: - [x] [Tabs][Android] unable to select over one or more TabStripItem NativeScript/NativeScript#7519 - [x] [BottomNavigation][Android]: crash when selecting tab with no correspondent item https://github.com/NativeScript/NativeScript/issues/7526 - [x] [Tabs][iOS] Incorrect tab bar when Tabs are wrapped in any layout - https://github.com/NativeScript/NativeScript/issues/7531 - [x] [iOS][Tabs] Unable to return to tab after tab with nested frame visited https://github.com/NativeScript/NativeScript/issues/7564 - [x] [BottomNavigation][iOS] created dynamically does not shows the TabStrip - NativeScript/NativeScript#7433 - [x] [Tabs][Android] TabStripItem selection highlight not visible with TabStripItem background color set - NativeScript/NativeScript#7494 - [x] [Tabs] TabStripItem color not applied - NativeScript/NativeScript#7495 - [x] [Tabs] Cannot change active text color for selected tabstripitem - NativeScript/NativeScript#7507 - [x] [BottomNav][Android] Changing tabs messes up text color of tabs - NativeScript/NativeScript#7623 - [x] [Tabs][BottomNavigation] TabContentItem background color not applied - NativeScript/NativeScript#7496 - [x] [Tabs][BottomNavigation] Font icon does not update to active state for selected tabstripitem - NativeScript/NativeScript#7506 - [x] [BottomNavigation] Renders bottom empty space if tabstrip is not defined - NativeScript/NativeScript#7471 - [x] [BottomNavigation][Tabs][iOS] TabStripItem font icon doesn't apply own classes - https://github.com/NativeScript/NativeScript/issues/7547 - [x] [iOS][Tabs][BottomNavigation] TabStrip.iosIconRenderingMode property value not respected https://github.com/NativeScript/NativeScript/issues/7733 - [x] [iOS][Tabs][BottomNavigation] Crash with TabStrip configured in code-behind https://github.com/NativeScript/NativeScript/issues/7692 - [x] [BottomNav] Clipped tabstripitem title content when iconSource specified https://github.com/NativeScript/NativeScript/issues/7713 Angular - [x] [Tabs] TabStripItem does not render - NativeScript/nativescript-angular#1884 - [x] [BottomNavigation] TabStripItem does not render - NativeScript/nativescript-angular#1893 - [x] Angular binding inside selectedIndexChanged event would work only if wrapped in NgZone - NativeScript/nativescript-angular#1896

NativeScript 6.2

Issues:

NativeScript vNext

BottomNavigation

Tabs

TabBars

  • [ ] BottomNavigationBar
  • [ ] TabsBar

Issues:

Contributing

If you want to work on a task from the list, tell us and we will do our best to help you!
If you have another great idea on how to make the tab view even cooler, share it in the comments!

Discussion

Please, don't report problems here. Instead, open a new issue and link it to this one.
Let's use this discussion for suggestions and improvement ideas. We would love to hear from you!


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

feature tabs

Most helpful comment

@jawa-the-hutt In short, you should use the BottomNavigation component. It doesn't pre-load tabs. Do you have an issue with it?

Otherwise, we tried disabling pre-load with the old TabView component. On Android it's not possible to tell the ViewPager to not pre-load tabs. We did a hacky workaround that proved to bring lots of bugs and problems.

This is part of the reason why we decided to split the tab navigation. The BottomNavigation component doesn't pre-load and doesn't have a swipe gesture. The Tabs pre-loads at least one tab to the sides and has the swipe gesture. Both have their use cases. We ultimately decided they can't be combined in one component safely.

All 30 comments

I expect ability to use FormattedString for TabStripItem. I think TabStripItem should extend TextBase. This would give full control over how it should look (because it behaves essentially like a Button).
I do not feel any special advantages over what was before.
For example, I need combine icon font, with formatted text in tab title.

@webleaf Unfortunately, customization options are greatly limited by the underlying native implementations. For example, on iOS the BottomNavigation TabStripItem is not even a UIView, so we can't really make it a TextBase. What we can do though, is provide the option to remove the bar and let you create your own bar with layouts and text views and animations and everything. Check out the PR - https://github.com/NativeScript/NativeScript/pull/7580. There is an example there. Will this work for you?

Hi guys,

I have a plugin nativescript-bottom-navigation based on material components that would be part of nativescript-material-components and I'm wondering if my plugin will give you issues because I'm registering the component as BottomNavigation for angular and in some projects the error below is appearing and another issue that already was addressed here

ERROR in ../node_modules/nativescript-angular/element-registry.js
Module not found: Error: Can't resolve 'tns-core-modules/ui/bottom-navigation' in 'BugRepo\node_modules\nativescript-angular'
 @ ../node_modules/nativescript-angular/element-registry.js 142:57-105
 @ ../node_modules/nativescript-angular/renderer.js
 @ ../node_modules/nativescript-angular/nativescript.module.js
 @ ./app/app.module.ts
 @ ./main.ts

Hi @henrychavez

This is a known error that occurs when you use a certain newer version of nativescript-angular and version pre 6.0 of the tns-core-modules. Either update nativescript-angular to the latest patch or update tns-core-modules to 6.0. The problem should go away.

There shouldn't be a problem with clashing names. You should be able to override the core modules elements. Both nativescript-angular and nativescript-vue should support overriding of registered elements, so the last one wins.

That being said, I think there actually might be a problem with your plugin's name and its functionality. From your code I see that you are exposing BottomNavigationBar components as in, only the bar, not the whole navigation component. It's a bit confusing, but the material guidelines declare BottomNavigation and Tabs more as patterns whereas the components are the MDCBottomNavigationBar and MDCTabBar. I'm saying this because for 6.1 we will expose the ability to use BottomNavigation, but remove the default bar and use your own - see https://github.com/NativeScript/NativeScript/pull/7580. If you keep the element name BottomNavigation for your plugin, it will override the core modules one and this won't be possible. If you rename it to BottomNavigationBar you will be able to combine the two and achieve more :) Let me know your thoughts.

Hi @MartoYankov,

I agree with you the docs are a little confusing related to BottomNavigation and Tabs that the material design team describe in their documentation.

I just review the code for bottom-navigation that you guys are doing and you are right, is better if I rename the plugin to BottomNavigationBar because I'm just exposing the component and leaving the implementation of navigation to the developer and you guys are preparing a more complete solution.

I like the possibility to combine both, so in that way, the developer can use the bottom-navigation-bar that fits their needs (you have a custom bar to support fonts and custom items, something that can't be done using the BottomNavigationView or MDCBottomNavigationBar provided by material components) that being said, the developer can use my plugin if they want to use what the material components provide us or your implementation of the bottom-navigation-bar if they want something more custom, am I right? :D

Hey @MartoYankov
When will this be a part of the tab template when we are creating a new app with tns create command? (It generates the template with the old TabView atm)

@vahidvdn Tab template should start generating app with BottomNavigation component for NativeScript 6.1 in September -- the changes are currently on PR https://github.com/NativeScript/nativescript-app-templates/pull/79

Is it expected that on iOS placing tabs inside some other element breaks the view ?
I am using old TabView for general navigation, and now in one of my pages i want to have another tabview ( inside a CardView ), I have used new Tabs item. It looks good(ish) on android but totally doesn't work on iOS - is the scenario i'm trying to achieve possible or it is expected to be impossible ? Same code produces following results on both platforms ( left iOS, right Android )
tabs-os-difference

@michalMajkel This is an interesting scenario. Can you try nesting the Tabs component in a layout. See this article about nesting - https://docs.nativescript.org/core-concepts/nested-navigation#simple-rule

Question: Is there a way to not preload tabs at all? Essentially setting the offscreenTabLimit to zero?. This would cause issues with swipe, but that could be disabled as well.

Possible use case would be providing three different views and subsequent actions based off the same data where by the data might be a different state that would cause it to show up in only one tab at a time.

For example. I have a list of Appointments and I'd like to have them show up in 3 tabs: Requested, Responded and Scheduled.

As it stands right now with a custom Tab UI implementation, when I load a tab, I go to the database and load the data. The query to the database prefiltrs the data so that it only brings the data it needs into the specific tab. I realize there are a lot of different ways I can do this, but this is the pattern being used and I wondered if it was possible to continue with the pattern.

The alternative is to load all the data in the parent tabs view, hold it somewhere and then have the specific tabs filter the data as we transition between them. This will work fine for us in our situation, but there might be a use case for someone else where this pattern doesn't work and they'd like to keep the database queries on a per tab basis.

@jawa-the-hutt In short, you should use the BottomNavigation component. It doesn't pre-load tabs. Do you have an issue with it?

Otherwise, we tried disabling pre-load with the old TabView component. On Android it's not possible to tell the ViewPager to not pre-load tabs. We did a hacky workaround that proved to bring lots of bugs and problems.

This is part of the reason why we decided to split the tab navigation. The BottomNavigation component doesn't pre-load and doesn't have a swipe gesture. The Tabs pre-loads at least one tab to the sides and has the swipe gesture. Both have their use cases. We ultimately decided they can't be combined in one component safely.

@MartoYankov. Thanks for the info. My use case calls for the Tabs to be just below the header in the app and not at the bottom. So...in a perfect world, I'd have the functionality of the bottom navigation, but be able to position it vertically wherever I wanted in the UI.

However, I do understand having to deal with the limitations of what the native platforms give us.

@jawa-the-hutt The way I understand it the reason why the BottomNavigation bar is on the bottom is that when you don't have the swipe gesture it's hard to reach to the top with your thumb to navigate.

That being said, the scenario you want should be possible with the BottomNavigation without (with a hidden) a TabStrip and use a custom tab bar on top. There are also a couple of plugins that provide tab bars that you can position anywhere.

@MartoYankov I wanted to follow back up on something in one of your comments above. You mentioned that the BottomNavigation component doesn't pre-load tabs. I'm finding that it does preload tabs. I can create a new issue if this is a bug, but first I wanted to make sure I understood you correctly.

What i'm seeing is that each tab is pre-loading. I'm using a simple console.log inside the Vue created lifecycle and each of those are logging at app startup. Here's a link to a playground.

Playground link

@jawa-the-hutt Vue (and Angular or any other framework for that matter) components have their own lifecycle that the native components don't affect. If you use the loaded event on each TabContentItem or on a element in the separate components, you will see when the UI is loaded on the native side.

Is there any plans to make it easy to change the tab highlight color ?

I ended up figuring out to do it after looking into the code, but I would expect it to be doable in css

current code:

<Tabs (loaded)="tabsViewLoaded($event)">
tabsViewLoaded(event) {
  const tabs: Tabs = event.object;
  tabs.setTabBarHighlightColor(new Color('rgb(81, 174, 88)'));
}

image

I'm feeling really frustrated with this. Label and Image CSS Styling is checked off for 6.1 but it seems really broken still on 6.1.2. And TabView completely breaks my app, so I'm kind of out of options.

@AnthonyLenglet Yes, there is a highlight-color property on the TabStrip element. You can use it in CSS. It seems we failed to document it. Will fix this.

@jlafitte Can you elaborate more on what you wanted to achieve, but couldn't? The TabStrip Label and Image styling is limited by the native components underneath. You can style background-color, color, font and text-transform. This brings feature parity with the styling of TabView.

Have to agree with @jlafitte . At least for iOS building with XCode11/iOS13 the control styling seems buggier than it was with 6.0. Far from my understanding of a RC release let alone an official release.

@PeterStaev It seems iOS 13 brought lots of breaking/behavior changes to lots of UIKit components. There are also problems with XCode11/iOS13 with our testing infrastructure too, which is the reason we couldn't extensively test everything. We are receiving increasing issue reports about this and we're aware of it. It's not connected to the BottomNav/Tabs components only.

Thanks @MartoYankov. Leave it to Apple/Google to mess up the things... 馃槃 I guess I will see to downgrade to XCode 10.3 and hope that Apple review wont deny the app release for not targeting iOS13 馃槣

I want to change tab indicator (bottom line) height (for android), how I can do that? Can someone please help me.

@AtoianAvetik

TabStrip{
    highlight-color: red;
}

They have updated the docs here

Thanks @felix-idf! But I mean the height(weight) of line, not a color :)

@AtoianAvetik This isn't supported at the moment. You can open a feature request for it.

@AtoianAvetik

TabStrip{
    highlight-color: red;
}

They have updated the docs here

it dont work

@mkrierQape Here is a playground link from the blog post - https://play.nativescript.org/?template=play-tsc&id=hhPr9s . The highlight color is changed to white.

Issue related nested routing on BottomNavigation using nativescript-angular

Navigation structure according this structure:

  • App-routing:
const routes: Routes = [
    { path: "welcome", component: WelcomeComponent },
    { path: "pages", loadChildren: () => import("./pages/pages.module").then(m => m.PagesModule) },
    { path: "", redirectTo: "welcome", pathMatch: "full" },
];
  • Pages-routing module
const routes: Routes = [
  {
    path: '',
    component: PagesComponent,
    children: [
      {
        path: 'home',
        outlet: 'homeoutlet',
        component: HomeComponent
      },
      {
        path: 'advices',
        outlet: 'advicesoutlet',
        component: AdvicesComponent
      },
      {
        path: 'alerts',
        outlet: 'alertsoutlet',
        component: AlertsComponent
      },
      { path: '', redirectTo: '/(homeoutlet:home//advicesoutlet:advices//alertsoutlet:alerts)', pathMatch: 'prefix' }
    ]
  }
];

When i am trying to navigate from /welcome route to pages/[path] route,
this.routerExtensions.navigate([{ outlets: { homeoutlet: ['pages', 'home'] } }]);

i get this error:
Error: ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'pages/home'

Playground example: https://play.nativescript.org/?template=play-ng&id=sVR895&v=3

@smorcuend

The path:'' shouldn't be empty when you use named outlet in your pages-routing.module.ts.
Just try path:'default' to have the outlet find the activeRoute.

Thanks

Just curios, is someone still working on the open issues? Or is the list outdated?

Was this page helpful?
0 / 5 - 0 ratings