Skip to content

Commit 4589431

Browse files
vtrifonovdtopuzov
andauthored
Tabs styling improvements (NativeScript#8366)
* fix(tabs): delay loadView when animation runs * chore: update api.md * chore: remove unnecessary casting * test: Added disabled test for changing tabs * tabs(ios): added tabs styling in ios * tabs: added iosAlignment property * tabs: textTransform support * tabs: iosAlignment moved to tabstrip * test: add frame-in-tabs test * chore: addressing PR comments * chore: addressing PR comments * chore: call method on the instance instead of call * chore: move IOSAlignment property * chore: update comments * fix: texttransform to tabstrip in bottomnavigation * chore: add new item to native-api-usage * chore: remove unneeded setNativeView call * chore: removed unneeded check Co-authored-by: Dimitar Topuzov <dtopuzov@gmail.com>
1 parent 286fcd3 commit 4589431

File tree

17 files changed

+472
-420
lines changed

17 files changed

+472
-420
lines changed

api-reports/NativeScript.api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,9 @@ export class iOSApplication {
12391239
window: any /* UIWindow */;
12401240
}
12411241

1242+
// @public
1243+
export type IOSTabBarItemsAlignment = "leading" | "justified" | "center" | "centerSelected";
1244+
12421245
// @public
12431246
export const isAndroid: boolean;
12441247

@@ -2274,6 +2277,8 @@ export class Tabs extends TabNavigationBase {
22742277

22752278
ios: any /* UITabBarController */;
22762279

2280+
iOSTabBarItemsAlignment: IOSTabBarItemsAlignment;
2281+
22772282
items: Array<TabContentItem>;
22782283

22792284
offscreenTabLimit: number;

e2e/ui-tests-app/app/tabs/main-page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function loadExamples() {
3232
examples.set("nested-layout", "tabs/nested-layout-page");
3333
examples.set("nested-bottom-navigation", "tabs/nested-bottom-navigation-page");
3434
examples.set("custom-tabstrip", "tabs/custom-tabstrip-page");
35+
examples.set("frame-in-tabs", "tabs/frame-in-tabs");
3536

3637
return examples;
3738
}

e2e/ui-tests-app/app/tabs/tab-strip-items-page.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<Page>
2-
<Tabs id="tabs" automationText="tabNavigation" >
2+
<Tabs id="tabs" automationText="tabNavigation" iOSTabBarItemsAlignment="leading">
33

44
<TabStrip>
55

nativescript-core/platforms/ios/native-api-usage.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"Foundation.*:*",
1515

1616
"MaterialComponents.MDCTabBar:*",
17+
"MaterialComponents.MDCTabBarIndicatorTemplate:*",
1718

1819
"NativeScriptEmbedder:*",
1920

nativescript-core/platforms/ios/typings/objc!MaterialComponents.d.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,13 +442,17 @@ declare class MDCTabBar extends UIView implements UIBarPositioning {
442442

443443
delegate: MDCTabBarDelegate;
444444

445-
displaysUppercaseTitles: boolean;
445+
displaysUppercaseTitles: boolean;
446446

447-
inkColor: UIColor;
447+
enableRippleBehavior: boolean;
448+
449+
inkColor: UIColor;
450+
451+
itemAppearance: MDCTabBarItemAppearance;
448452

449-
itemAppearance: MDCTabBarItemAppearance;
453+
items: NSArray<UITabBarItem>;
450454

451-
items: NSArray<UITabBarItem>;
455+
rippleColor: UIColor;
452456

453457
selectedItem: UITabBarItem;
454458

nativescript-core/ui/bottom-navigation/bottom-navigation.android.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ export class BottomNavigation extends TabNavigationBase {
248248
private _currentTransaction: androidx.fragment.app.FragmentTransaction;
249249
private _attachedToWindow = false;
250250
public _originalBackground: any;
251+
private _textTransform: TextTransform = "none";
251252

252253
constructor() {
253254
super();
@@ -567,6 +568,18 @@ export class BottomNavigation extends TabNavigationBase {
567568
});
568569
}
569570

571+
private getItemLabelTextTransform(tabStripItem: TabStripItem): TextTransform {
572+
const nestedLabel = tabStripItem.label;
573+
let textTransform: TextTransform = null;
574+
if (nestedLabel && nestedLabel.style.textTransform !== "initial") {
575+
textTransform = nestedLabel.style.textTransform;
576+
} else if (tabStripItem.style.textTransform !== "initial") {
577+
textTransform = tabStripItem.style.textTransform;
578+
}
579+
580+
return textTransform || this._textTransform;
581+
}
582+
570583
private createTabItemSpec(tabStripItem: TabStripItem): org.nativescript.widgets.TabItemSpec {
571584
const tabItemSpec = new org.nativescript.widgets.TabItemSpec();
572585

@@ -575,10 +588,8 @@ export class BottomNavigation extends TabNavigationBase {
575588
let title = titleLabel.text;
576589

577590
// TEXT-TRANSFORM
578-
const textTransform = titleLabel.style.textTransform;
579-
if (textTransform) {
580-
title = getTransformedText(title, textTransform);
581-
}
591+
const textTransform = this.getItemLabelTextTransform(tabStripItem);
592+
title = getTransformedText(title, textTransform);
582593
tabItemSpec.title = title;
583594

584595
// BACKGROUND-COLOR
@@ -736,6 +747,24 @@ export class BottomNavigation extends TabNavigationBase {
736747
tabStripItem.nativeViewProtected.setText(title);
737748
}
738749

750+
public getTabBarTextTransform(): TextTransform {
751+
return this._textTransform;
752+
}
753+
754+
public setTabBarTextTransform(value: TextTransform): void {
755+
let items = this.tabStrip && this.tabStrip.items;
756+
if (items) {
757+
items.forEach((tabStripItem) => {
758+
if (tabStripItem.label && tabStripItem.nativeViewProtected) {
759+
const nestedLabel = tabStripItem.label;
760+
const title = getTransformedText(nestedLabel.text, value);
761+
tabStripItem.nativeViewProtected.setText(title);
762+
}
763+
});
764+
}
765+
this._textTransform = value;
766+
}
767+
739768
[selectedIndexProperty.setNative](value: number) {
740769
// const smoothScroll = false;
741770

nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts

Lines changed: 37 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,19 @@ class UITabBarControllerImpl extends UITabBarController {
7171

7272
public viewWillTransitionToSizeWithTransitionCoordinator(size: CGSize, coordinator: UIViewControllerTransitionCoordinator): void {
7373
super.viewWillTransitionToSizeWithTransitionCoordinator(size, coordinator);
74-
UIViewControllerTransitionCoordinator.prototype.animateAlongsideTransitionCompletion
75-
.call(coordinator, () => {
76-
const owner = this._owner.get();
77-
if (owner && owner.tabStrip && owner.tabStrip.items) {
78-
const tabStrip = owner.tabStrip;
79-
tabStrip.items.forEach(tabStripItem => {
80-
updateBackgroundPositions(tabStrip, tabStripItem);
81-
82-
const index = tabStripItem._index;
83-
const tabBarItemController = this.viewControllers[index];
84-
updateTitleAndIconPositions(tabStripItem, tabBarItemController.tabBarItem, tabBarItemController);
85-
});
86-
}
87-
}, null);
74+
coordinator.animateAlongsideTransitionCompletion(() => {
75+
const owner = this._owner.get();
76+
if (owner && owner.tabStrip && owner.tabStrip.items) {
77+
const tabStrip = owner.tabStrip;
78+
tabStrip.items.forEach(tabStripItem => {
79+
updateBackgroundPositions(tabStrip, tabStripItem);
80+
81+
const index = tabStripItem._index;
82+
const tabBarItemController = this.viewControllers[index];
83+
updateTitleAndIconPositions(tabStripItem, tabBarItemController.tabBarItem, tabBarItemController);
84+
});
85+
}
86+
}, null);
8887
}
8988

9089
// Mind implementation for other controllers
@@ -207,17 +206,14 @@ class UINavigationControllerDelegateImpl extends NSObject implements UINavigatio
207206

208207
function updateBackgroundPositions(tabStrip: TabStrip, tabStripItem: TabStripItem) {
209208
let bgView = (<any>tabStripItem).bgView;
209+
const index = tabStripItem._index;
210+
const width = tabStrip.nativeView.frame.size.width / tabStrip.items.length;
211+
const frame = CGRectMake(width * index, 0, width, tabStrip.nativeView.frame.size.width);
210212
if (!bgView) {
211-
const index = tabStripItem._index;
212-
const width = tabStrip.nativeView.frame.size.width / tabStrip.items.length;
213-
const frame = CGRectMake(width * index, 0, width, tabStrip.nativeView.frame.size.width);
214213
bgView = UIView.alloc().initWithFrame(frame);
215214
tabStrip.nativeView.insertSubviewAtIndex(bgView, 0);
216215
(<any>tabStripItem).bgView = bgView;
217216
} else {
218-
const index = tabStripItem._index;
219-
const width = tabStrip.nativeView.frame.size.width / tabStrip.items.length;
220-
const frame = CGRectMake(width * index, 0, width, tabStrip.nativeView.frame.size.width);
221217
bgView.frame = frame;
222218
}
223219

@@ -376,8 +372,7 @@ export class BottomNavigation extends TabNavigationBase {
376372
}
377373

378374
public setTabBarItemColor(tabStripItem: TabStripItem, value: UIColor | Color): void {
379-
const states = getTitleAttributesForStates(tabStripItem.label);
380-
applyStatesToItem(tabStripItem.nativeView, states, this.viewController.tabBar);
375+
setViewTextAttributes(tabStripItem.nativeView, tabStripItem.label, this.viewController.tabBar);
381376
}
382377

383378
public setTabBarIconColor(tabStripItem: TabStripItem, value: UIColor | Color): void {
@@ -388,15 +383,23 @@ export class BottomNavigation extends TabNavigationBase {
388383
}
389384

390385
public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void {
391-
const states = getTitleAttributesForStates(tabStripItem.label);
392-
applyStatesToItem(tabStripItem.nativeView, states, this.viewController.tabBar);
386+
setViewTextAttributes(tabStripItem.nativeView, tabStripItem.label, this.viewController.tabBar);
393387
}
394388

395389
public setTabBarItemTextTransform(tabStripItem: TabStripItem, value: TextTransform): void {
396390
const title = getTransformedText(tabStripItem.label.text, value);
397391
tabStripItem.nativeView.title = title;
398392
}
399393

394+
public getTabBarHighlightColor(): UIColor {
395+
return this._ios.tabBar.tintColor;
396+
}
397+
398+
public setTabBarHighlightColor(value: UIColor | Color) {
399+
const nativeColor = value instanceof Color ? value.ios : value;
400+
this._ios.tabBar.tintColor = nativeColor;
401+
}
402+
400403
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
401404
const width = layout.getMeasureSpecSize(widthMeasureSpec);
402405
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
@@ -518,8 +521,7 @@ export class BottomNavigation extends TabNavigationBase {
518521
const tabBarItem = this.createTabBarItem(tabStripItem, i);
519522
updateTitleAndIconPositions(tabStripItem, tabBarItem, controller);
520523

521-
const states = getTitleAttributesForStates(tabStripItem.label);
522-
applyStatesToItem(tabBarItem, states, this.viewController.tabBar);
524+
setViewTextAttributes(tabBarItem, tabStripItem.label, this.viewController.tabBar);
523525

524526
controller.tabBarItem = tabBarItem;
525527
tabStripItem._index = i;
@@ -688,51 +690,31 @@ export class BottomNavigation extends TabNavigationBase {
688690
}
689691
}
690692

691-
interface TabStates {
692-
normalState?: any;
693-
selectedState?: any;
694-
}
695-
696-
function getTitleAttributesForStates(view: View): TabStates {
693+
function setViewTextAttributes(item: UITabBarItem, view: View, tabBar: UITabBar): any {
697694
if (!view) {
698695
return null;
699696
}
700697

701-
const result: TabStates = {};
702698
const defaultTabItemFontSize = 10;
703699
const tabItemFontSize = view.style.fontSize || defaultTabItemFontSize;
704700
const font: UIFont = view.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize));
705701
const tabItemTextColor = view.style.color;
706702
const textColor = tabItemTextColor instanceof Color ? tabItemTextColor.ios : null;
707-
result.normalState = { [NSFontAttributeName]: font };
703+
let attributes: any = { [NSFontAttributeName]: font };
708704
if (textColor) {
709-
result.normalState[UITextAttributeTextColor] = textColor;
705+
attributes[UITextAttributeTextColor] = textColor;
706+
attributes[NSForegroundColorAttributeName] = textColor;
710707
}
711708

712-
const tabSelectedItemTextColor = view.style.color;
713-
const selectedTextColor = tabSelectedItemTextColor instanceof Color ? tabSelectedItemTextColor.ios : null;
714-
result.selectedState = { [NSFontAttributeName]: font };
715-
if (selectedTextColor) {
716-
result.selectedState[UITextAttributeTextColor] = selectedTextColor;
717-
}
718-
719-
return result;
720-
}
721-
722-
function applyStatesToItem(item: UITabBarItem, states: TabStates, tabBar: UITabBar) {
723-
if (!states) {
724-
return;
725-
}
726-
727-
item.setTitleTextAttributesForState(states.normalState, UIControlState.Normal);
728-
item.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected);
709+
item.setTitleTextAttributesForState(attributes, UIControlState.Selected);
710+
item.setTitleTextAttributesForState(attributes, UIControlState.Normal);
729711

730712
// there's a bug when setting the item color on ios 13 if there's no background set to the tabstrip
731713
// https://books.google.bg/books?id=99_BDwAAQBAJ&q=tabBar.unselectedItemTintColor
732714
// to fix the above issue we are applying the selected fix only for the case, when there is no background set
733715
// in that case we have the following known issue:
734716
// we will set the color to all unselected items, so you won't be able to set different colors for the different not selected items
735-
if (!tabBar.barTintColor && states.normalState[UITextAttributeTextColor] && (majorVersion > 9)) {
736-
tabBar.unselectedItemTintColor = states.normalState[UITextAttributeTextColor];
717+
if (!tabBar.barTintColor && attributes[UITextAttributeTextColor] && (majorVersion > 9)) {
718+
tabBar.unselectedItemTintColor = attributes[UITextAttributeTextColor];
737719
}
738-
}
720+
}

nativescript-core/ui/core/view/view-common.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { sanitizeModuleName } from "../../builder/module-name-sanitizer";
2626
import { StyleScope } from "../../styling/style-scope";
2727
import { LinearGradient } from "../../styling/linear-gradient";
2828
import { BackgroundRepeat } from "../../styling/style-properties";
29+
import { TextTransform } from "../../text-base";
2930

3031
export * from "../../styling/style-properties";
3132
export * from "../view-base";
@@ -718,6 +719,13 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
718719
this.style.perspective = value;
719720
}
720721

722+
get textTransform(): TextTransform {
723+
return this.style.textTransform;
724+
}
725+
set textTransform(value: TextTransform) {
726+
this.style.textTransform = value;
727+
}
728+
721729
get translateX(): dip {
722730
return this.style.translateX;
723731
}

nativescript-core/ui/core/view/view.ios.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export class View extends ViewCommon implements ViewDefinition {
180180
if (adjustedFrame) {
181181
nativeView.frame = adjustedFrame;
182182
}
183-
183+
184184
if (this._hasTransfrom) {
185185
// re-apply the transform after the frame is adjusted
186186
nativeView.layer.transform = transform;
@@ -359,7 +359,7 @@ export class View extends ViewCommon implements ViewDefinition {
359359
if (this.rotateX || this.rotateY) {
360360
transform.m34 = -1 / perspective;
361361
}
362-
362+
363363
transform = CATransform3DTranslate(transform, this.translateX, this.translateY, 0);
364364
transform = iosNativeHelper.applyRotateTransform(transform, this.rotateX, this.rotateY, this.rotate);
365365
transform = CATransform3DScale(transform, scaleX, scaleY, 1);
@@ -479,8 +479,7 @@ export class View extends ViewCommon implements ViewDefinition {
479479
parentController.presentViewControllerAnimatedCompletion(controller, animated, null);
480480
const transitionCoordinator = parentController.transitionCoordinator;
481481
if (transitionCoordinator) {
482-
UIViewControllerTransitionCoordinator.prototype.animateAlongsideTransitionCompletion
483-
.call(transitionCoordinator, null, () => this._raiseShownModallyEvent());
482+
transitionCoordinator.animateAlongsideTransitionCompletion(null, () => this._raiseShownModallyEvent());
484483
} else {
485484
// Apparently iOS 9+ stops all transitions and animations upon application suspend and transitionCoordinator becomes null here in this case.
486485
// Since we are not waiting for any transition to complete, i.e. transitionCoordinator is null, we can directly raise our shownModally event.

nativescript-core/ui/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export { TabNavigationBase } from "./tab-navigation-base/tab-navigation-base";
3434
export { TabStrip, TabStripItemEventData } from "./tab-navigation-base/tab-strip";
3535
export { TabStripItem } from "./tab-navigation-base/tab-strip-item";
3636
export { TabView, TabViewItem } from "./tab-view";
37-
export { Tabs } from "./tabs";
37+
export { Tabs, IOSTabBarItemsAlignment } from "./tabs";
3838
export { TextBase } from "./text-base";
3939
export { FormattedString } from "./text-base/formatted-string";
4040
export { Span } from "./text-base/span";

nativescript-core/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,4 +286,4 @@ export const tabStripProperty = new Property<TabNavigationBase, TabStrip>({
286286
target.onTabStripChanged(oldValue, newValue);
287287
}
288288
});
289-
tabStripProperty.register(TabNavigationBase);
289+
tabStripProperty.register(TabNavigationBase);

nativescript-core/ui/tab-navigation-base/tab-strip-item/tab-strip-item.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { isIOS } from "../../../platform";
1414
import { Image } from "../../image/image";
1515
import { Label } from "../../label/label";
16+
import { textTransformProperty, TextTransform } from "../../text-base";
1617

1718
export * from "../../core/view";
1819
export const traceCategory = "TabView";
@@ -236,6 +237,19 @@ export class TabStripItem extends View implements TabStripItemDefinition, AddChi
236237
return tabStripParent && tabStripParent.setTabBarItemBackgroundColor(this, value);
237238
}
238239

240+
[textTransformProperty.getDefault](): TextTransform {
241+
const parent = <TabStrip>this.parent;
242+
const tabStripParent = parent && <TabNavigationBase>parent.parent;
243+
244+
return tabStripParent && tabStripParent.getTabBarItemTextTransform(this);
245+
}
246+
[textTransformProperty.setNative](value: TextTransform) {
247+
const parent = <TabStrip>this.parent;
248+
const tabStripParent = parent && <TabNavigationBase>parent.parent;
249+
250+
return tabStripParent && tabStripParent.setTabBarItemTextTransform(this, value);
251+
}
252+
239253
[backgroundInternalProperty.getDefault](): any {
240254
return null;
241255
}

0 commit comments

Comments
 (0)