mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
refactor(tabs): strip item appearance and creation (#7466)
* test(e2e): add tab strip items pages * fix(ios-tabs): a crash on swiping a content item Fixes https://github.com/NativeScript/NativeScript/issues/7459. * feat(android-tabs): create tab item spec from image and label Implements https://github.com/NativeScript/nativescript-angular/issues/1884 for Android. * feat(ios-tabs): tab bar item appearance Implements https://github.com/NativeScript/NativeScript/issues/7436 and https://github.com/NativeScript/nativescript-angular/issues/1884. * refactor(ios-tabs): move _hasImage and _hasTitle properties
This commit is contained in:

committed by
Alexander Djenkov

parent
c42bf0a6d0
commit
c34a48ac77
@ -1,8 +1,9 @@
|
|||||||
import { EventData } from "tns-core-modules/data/observable";
|
import { EventData } from "tns-core-modules/data/observable";
|
||||||
import { SubMainPageViewModel } from "../sub-main-page-view-model";
|
|
||||||
import { WrapLayout } from "tns-core-modules/ui/layouts/wrap-layout";
|
import { WrapLayout } from "tns-core-modules/ui/layouts/wrap-layout";
|
||||||
import { Page } from "tns-core-modules/ui/page";
|
import { Page } from "tns-core-modules/ui/page";
|
||||||
|
|
||||||
|
import { SubMainPageViewModel } from "../sub-main-page-view-model";
|
||||||
|
|
||||||
export function pageLoaded(args: EventData) {
|
export function pageLoaded(args: EventData) {
|
||||||
const page = <Page>args.object;
|
const page = <Page>args.object;
|
||||||
const wrapLayout = <WrapLayout>page.getViewById("wrapLayoutWithExamples");
|
const wrapLayout = <WrapLayout>page.getViewById("wrapLayoutWithExamples");
|
||||||
@ -18,6 +19,8 @@ export function loadExamples() {
|
|||||||
examples.set("icon-title-placement", "tabs/icon-title-placement");
|
examples.set("icon-title-placement", "tabs/icon-title-placement");
|
||||||
examples.set("icon-change", "tabs/icon-change");
|
examples.set("icon-change", "tabs/icon-change");
|
||||||
examples.set("swipe-enabled", "tabs/swipe-enabled");
|
examples.set("swipe-enabled", "tabs/swipe-enabled");
|
||||||
|
examples.set("strip-item", "tabs/tab-strip-item-page");
|
||||||
|
examples.set("strip-items", "tabs/tab-strip-items-page");
|
||||||
examples.set("tabs-position", "tabs/tabs-position-page");
|
examples.set("tabs-position", "tabs/tabs-position-page");
|
||||||
examples.set("tabs-binding", "tabs/tabs-binding-page");
|
examples.set("tabs-binding", "tabs/tabs-binding-page");
|
||||||
|
|
||||||
|
16
e2e/ui-tests-app/app/tabs/tab-strip-item-page.xml
Normal file
16
e2e/ui-tests-app/app/tabs/tab-strip-item-page.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<Page>
|
||||||
|
<Tabs id="tabs">
|
||||||
|
|
||||||
|
<TabStrip>
|
||||||
|
|
||||||
|
<TabStripItem title="TabStripItem 1" iconSource="res://icon">
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
</TabStrip>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 1"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
</Page>
|
88
e2e/ui-tests-app/app/tabs/tab-strip-items-page.xml
Normal file
88
e2e/ui-tests-app/app/tabs/tab-strip-items-page.xml
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<Page>
|
||||||
|
<Tabs id="tabs">
|
||||||
|
|
||||||
|
<TabStrip>
|
||||||
|
|
||||||
|
<TabStripItem title="TabStripItem 1" iconSource="res://icon">
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem>
|
||||||
|
<Label text="TabStripItem 2">
|
||||||
|
</Label>
|
||||||
|
<Image src="res://icon">
|
||||||
|
</Image>
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem title="TabStripItem X" iconSource="res://icon">
|
||||||
|
<Label text="TabStripItem 3">
|
||||||
|
</Label>
|
||||||
|
<Image src="res://icon">
|
||||||
|
</Image>
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem>
|
||||||
|
<Label text="TabStripItem 4">
|
||||||
|
</Label>
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem>
|
||||||
|
<Image src="res://icon">
|
||||||
|
</Image>
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem title="TabStripItem 6">
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem iconSource="res://icon">
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem title="TabStripItem X" iconSource="res://icon">
|
||||||
|
<Label text="TabStripItem 8">
|
||||||
|
</Label>
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
<TabStripItem title="TabStripItem 9" iconSource="res://icon">
|
||||||
|
<Image src="res://icon">
|
||||||
|
</Image>
|
||||||
|
</TabStripItem>
|
||||||
|
|
||||||
|
</TabStrip>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 1"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 2"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 3"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 4"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 5"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 6"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 7"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 8"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
<TabContentItem>
|
||||||
|
<Label text="TabContentItem 9"></Label>
|
||||||
|
</TabContentItem>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
</Page>
|
@ -4,6 +4,8 @@
|
|||||||
*/ /** */
|
*/ /** */
|
||||||
|
|
||||||
import { View, EventData } from "../../core/view";
|
import { View, EventData } from "../../core/view";
|
||||||
|
import { Image } from "../../image/image";
|
||||||
|
import { Label } from "../../label/label";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a tab strip entry.
|
* Represents a tab strip entry.
|
||||||
@ -19,6 +21,16 @@ export class TabStripItem extends View {
|
|||||||
*/
|
*/
|
||||||
iconSource: string;
|
iconSource: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or sets the label of the tab strip entry.
|
||||||
|
*/
|
||||||
|
label: Label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or sets the image of the tab strip entry.
|
||||||
|
*/
|
||||||
|
image: Image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String value used when hooking to the tap event.
|
* String value used when hooking to the tap event.
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +21,16 @@ export class TabStrip extends View {
|
|||||||
* Gets or sets the icon rendering mode on iOS
|
* Gets or sets the icon rendering mode on iOS
|
||||||
*/
|
*/
|
||||||
iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate";
|
iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_hasImage: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_hasTitle: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const iosIconRenderingModeProperty: Property<TabStrip, "automatic" | "alwaysOriginal" | "alwaysTemplate">;
|
export const iosIconRenderingModeProperty: Property<TabStrip, "automatic" | "alwaysOriginal" | "alwaysTemplate">;
|
||||||
|
@ -14,6 +14,8 @@ export const traceCategory = "TabView";
|
|||||||
export class TabStrip extends View implements TabStripDefinition, AddChildFromBuilder, AddArrayFromBuilder {
|
export class TabStrip extends View implements TabStripDefinition, AddChildFromBuilder, AddArrayFromBuilder {
|
||||||
public items: TabStripItem[];
|
public items: TabStripItem[];
|
||||||
public iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate";
|
public iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate";
|
||||||
|
public _hasImage: boolean;
|
||||||
|
public _hasTitle: boolean;
|
||||||
|
|
||||||
public eachChild(callback: (child: ViewBase) => boolean) {
|
public eachChild(callback: (child: ViewBase) => boolean) {
|
||||||
const items = this.items;
|
const items = this.items;
|
||||||
|
@ -234,29 +234,34 @@ function initializeNativeClasses() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createTabItemSpec(item: TabStripItem): org.nativescript.widgets.TabItemSpec {
|
function createTabItemSpec(item: TabStripItem): org.nativescript.widgets.TabItemSpec {
|
||||||
const result = new org.nativescript.widgets.TabItemSpec();
|
let iconSource;
|
||||||
result.title = item.title;
|
const tabItemSpec = new org.nativescript.widgets.TabItemSpec();
|
||||||
|
|
||||||
if (item.iconSource) {
|
// Image and Label children of TabStripItem
|
||||||
if (item.iconSource.indexOf(RESOURCE_PREFIX) === 0) {
|
// take priority over its `iconSource` and `title` properties
|
||||||
result.iconId = ad.resources.getDrawableId(item.iconSource.substr(RESOURCE_PREFIX.length));
|
iconSource = item.image ? item.image.src : item.iconSource;
|
||||||
if (result.iconId === 0) {
|
tabItemSpec.title = item.label ? item.label.text : item.title;
|
||||||
|
|
||||||
|
if (iconSource) {
|
||||||
|
if (iconSource.indexOf(RESOURCE_PREFIX) === 0) {
|
||||||
|
tabItemSpec.iconId = ad.resources.getDrawableId(iconSource.substr(RESOURCE_PREFIX.length));
|
||||||
|
if (tabItemSpec.iconId === 0) {
|
||||||
// TODO
|
// TODO
|
||||||
// traceMissingIcon(item.iconSource);
|
// traceMissingIcon(iconSource);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const is = fromFileOrResource(item.iconSource);
|
const is = fromFileOrResource(iconSource);
|
||||||
if (is) {
|
if (is) {
|
||||||
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
|
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
|
||||||
result.iconDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), is.android);
|
tabItemSpec.iconDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), is.android);
|
||||||
} else {
|
} else {
|
||||||
// TODO
|
// TODO
|
||||||
// traceMissingIcon(item.iconSource);
|
// traceMissingIcon(iconSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return tabItemSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
let defaultAccentColor: number = undefined;
|
let defaultAccentColor: number = undefined;
|
||||||
|
10
tns-core-modules/ui/tabs/tabs.d.ts
vendored
10
tns-core-modules/ui/tabs/tabs.d.ts
vendored
@ -1,11 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Contains the TabView class, which represents a standard content component with tabs.
|
* Contains the Tabs class, which represents a tab navigation component.
|
||||||
* @module "ui/tab-view"
|
* @module "ui/tabs"
|
||||||
*/ /** */
|
*/ /** */
|
||||||
|
|
||||||
import { Property, EventData } from "../core/view";
|
import { EventData, Property } from "../core/view";
|
||||||
import { TabNavigationBase, SelectedIndexChangedEventData } from "../tab-navigation-base/tab-navigation-base";
|
|
||||||
import { TabContentItem } from "../tab-navigation-base/tab-content-item";
|
import { TabContentItem } from "../tab-navigation-base/tab-content-item";
|
||||||
|
import {
|
||||||
|
SelectedIndexChangedEventData, TabNavigationBase
|
||||||
|
} from "../tab-navigation-base/tab-navigation-base";
|
||||||
import { TabStrip } from "../tab-navigation-base/tab-strip";
|
import { TabStrip } from "../tab-navigation-base/tab-strip";
|
||||||
|
|
||||||
export * from "../tab-navigation-base/tab-content-item";
|
export * from "../tab-navigation-base/tab-content-item";
|
||||||
|
@ -81,23 +81,11 @@ class UIPageViewControllerImpl extends UIPageViewController {
|
|||||||
tabBar.items = NSArray.arrayWithArray(tabBarItems);
|
tabBar.items = NSArray.arrayWithArray(tabBarItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tabBar.items = <NSArray<UITabBarItem>>NSArray.alloc().initWithArray([
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
tabBar.delegate = this.tabBarDelegate = MDCTabBarDelegateImpl.initWithOwner(new WeakRef(owner));
|
tabBar.delegate = this.tabBarDelegate = MDCTabBarDelegateImpl.initWithOwner(new WeakRef(owner));
|
||||||
tabBar.itemAppearance = MDCTabBarItemAppearance.Titles;
|
// Initially set `itemAppearance` to TitledImages.
|
||||||
|
// Reassign if needed when items available.
|
||||||
|
// Other combinations do not work.
|
||||||
|
tabBar.itemAppearance = MDCTabBarItemAppearance.TitledImages;
|
||||||
tabBar.tintColor = UIColor.blueColor;
|
tabBar.tintColor = UIColor.blueColor;
|
||||||
tabBar.barTintColor = UIColor.whiteColor;
|
tabBar.barTintColor = UIColor.whiteColor;
|
||||||
tabBar.setTitleColorForState(UIColor.blackColor, MDCTabBarItemState.Normal);
|
tabBar.setTitleColorForState(UIColor.blackColor, MDCTabBarItemState.Normal);
|
||||||
@ -856,33 +844,20 @@ export class Tabs extends TabsBase {
|
|||||||
public setTabStripItems(items: Array<TabStripItem>) {
|
public setTabStripItems(items: Array<TabStripItem>) {
|
||||||
const tabBarItems = [];
|
const tabBarItems = [];
|
||||||
|
|
||||||
items.forEach((item: TabStripItem, i, arr) => {
|
items.forEach((item: TabStripItem, i) => {
|
||||||
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(item.title, null, 0);
|
const tabBarItem = this.createTabBarItem(item, i);
|
||||||
tabBarItems.push(tabBarItem);
|
tabBarItems.push(tabBarItem);
|
||||||
item.setNativeView(tabBarItem);
|
item.setNativeView(tabBarItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tabBarItems = tabBarItems;
|
this.tabBarItems = tabBarItems;
|
||||||
|
|
||||||
if (this.viewController && this.viewController.tabBar) {
|
if (this.viewController && this.viewController.tabBar) {
|
||||||
|
this.viewController.tabBar.itemAppearance = this._getTabBarItemAppearance();
|
||||||
this.viewController.tabBar.items = NSArray.arrayWithArray(tabBarItems);
|
this.viewController.tabBar.items = NSArray.arrayWithArray(tabBarItems);
|
||||||
this.tabStrip.setNativeView(this.viewController.tabBar);
|
this.tabStrip.setNativeView(this.viewController.tabBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tabBar.items = <NSArray<UITabBarItem>>NSArray.alloc().initWithArray([
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// UITabBarItem.alloc().initWithTitleImageTag("Test", null, 0),
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// const length = items ? items.length : 0;
|
// const length = items ? items.length : 0;
|
||||||
// if (length === 0) {
|
// if (length === 0) {
|
||||||
// this._tabLayout.setItems(null, null);
|
// this._tabLayout.setItems(null, null);
|
||||||
@ -905,6 +880,41 @@ export class Tabs extends TabsBase {
|
|||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createTabBarItem(item: TabStripItem, index: number): UITabBarItem {
|
||||||
|
let image: UIImage;
|
||||||
|
let title: string;
|
||||||
|
|
||||||
|
// Image and Label children of TabStripItem
|
||||||
|
// take priority over its `iconSource` and `title` properties
|
||||||
|
image = item.image ? this._getIcon(item.image.src) : this._getIcon(item.iconSource);
|
||||||
|
title = item.label ? item.label.text : item.title;
|
||||||
|
|
||||||
|
if (!this.tabStrip._hasImage) {
|
||||||
|
this.tabStrip._hasImage = !!image;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.tabStrip._hasTitle) {
|
||||||
|
this.tabStrip._hasTitle = !!title;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(title, image, index);
|
||||||
|
|
||||||
|
return tabBarItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getTabBarItemAppearance(): MDCTabBarItemAppearance {
|
||||||
|
let itemAppearance;
|
||||||
|
if (this.tabStrip._hasImage && this.tabStrip._hasTitle) {
|
||||||
|
itemAppearance = MDCTabBarItemAppearance.TitledImages;
|
||||||
|
} else if (this.tabStrip._hasImage) {
|
||||||
|
itemAppearance = MDCTabBarItemAppearance.Images;
|
||||||
|
} else {
|
||||||
|
itemAppearance = MDCTabBarItemAppearance.Titles;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemAppearance;
|
||||||
|
}
|
||||||
|
|
||||||
private _getIconRenderingMode(): UIImageRenderingMode {
|
private _getIconRenderingMode(): UIImageRenderingMode {
|
||||||
return UIImageRenderingMode.AlwaysOriginal;
|
return UIImageRenderingMode.AlwaysOriginal;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user