import { themer } from '@nativescript-community/ui-material-core' ; import { Color , Device , Font , Frame , IOSHelper , ImageSource , Property , Trace , Utils , View , ViewBase , booleanConverter } from '@nativescript/core' ; import { TabsBase , swipeEnabledProperty } from './tabs-common' ; import { getIconSpecSize , itemsProperty , selectedIndexProperty , tabStripProperty } from '@nativescript-community/ui-material-core/tab-navigation-base/tab-navigation-base' ; import { TabStrip } from '@nativescript-community/ui-material-core/tab-navigation-base/tab-strip' ; import { TabStripItem } from '@nativescript-community/ui-material-core/tab-navigation-base/tab-strip-item' ; import { TabContentItem } from '@nativescript-community/ui-material-core/tab-navigation-base/tab-content-item' ; export { TabContentItem , TabStrip , TabStripItem } ; const majorVersion = Utils . ios . MajorVersion ; const isPhone = Device . deviceType === 'Phone' ; const invokeOnRunLoop = ( function ( ) { const runloop = CFRunLoopGetMain ( ) ; return ( action ) => { CFRunLoopPerformBlock ( runloop , kCFRunLoopDefaultMode , action ) ; CFRunLoopWakeUp ( runloop ) ; } ; } ) ( ) ; @ NativeClass class MDCTabBarViewDelegateImpl extends NSObject implements MDCTabBarViewDelegate { public static ObjCProtocols = [ MDCTabBarViewDelegate ] ; private _owner : WeakRef < Tabs > ; public static initWithOwner ( owner ) { const delegate = MDCTabBarViewDelegateImpl . new ( ) as MDCTabBarViewDelegateImpl ; delegate . _owner = owner ; return delegate ; } public tabBarViewShouldSelectItem ( tabBar , item ) { const owner = this . _owner . get ( ) ; const shouldSelectItem = owner . _canSelectItem ; const selectedIndex = owner . tabBarItems . indexOf ( item ) ; if ( owner . selectedIndex !== selectedIndex ) { owner . _canSelectItem = false ; } const tabStrip = owner . tabStrip ; const tabStripItems = tabStrip && tabStrip . items ; if ( tabStripItems && tabStripItems [ selectedIndex ] ) { tabStripItems [ selectedIndex ] . _emit ( TabStripItem . tapEvent ) ; tabStrip . notify ( { eventName : TabStrip . itemTapEvent , object : tabStrip , index : selectedIndex } ) ; } return shouldSelectItem ; } public tabBarViewDidSelectItem ( tabBar , selectedItem ) { const owner = this . _owner . get ( ) ; const tabBarItems = owner . tabBarItems ; const selectedIndex = tabBarItems . indexOf ( selectedItem ) ; owner . selectedIndex = selectedIndex ; } } @ NativeClass class BackgroundIndicatorTemplate extends NSObject implements MDCTabBarViewIndicatorTemplate { public static ObjCProtocols = [ MDCTabBarViewIndicatorTemplate ] ; public indicatorAttributesForContext ( context ) { const attributes = new MDCTabBarViewIndicatorAttributes ( ) ; attributes . path = UIBezierPath . bezierPathWithRect ( context . bounds ) ; return attributes ; } } @ NativeClass class UIPageViewControllerImpl extends UIPageViewController { tabBar : MDCTabBarView ; scrollView : UIScrollView ; tabBarDelegate : MDCTabBarViewDelegateImpl ; private _owner : WeakRef < Tabs > ; public static initWithOwner ( owner ) { const handler = UIPageViewControllerImpl . alloc ( ) . initWithTransitionStyleNavigationOrientationOptions ( UIPageViewControllerTransitionStyle . Scroll , UIPageViewControllerNavigationOrientation . Horizontal , null ) as UIPageViewControllerImpl ; handler . _owner = owner ; return handler ; } public viewDidLoad ( ) { const owner = this . _owner . get ( ) ; const tabBarItems = owner . tabBarItems ; const tabBar = MDCTabBarView . alloc ( ) . init ( ) ; this . tabBar = tabBar ; const colorScheme = themer . getAppColorScheme ( ) as MDCSemanticColorScheme ; if ( tabBarItems && tabBarItems . length ) { tabBar . items = NSArray . arrayWithArray ( tabBarItems ) ; } tabBar . tabBarDelegate = this . tabBarDelegate = MDCTabBarViewDelegateImpl . initWithOwner ( new WeakRef ( owner ) ) ; if ( colorScheme && colorScheme . primaryColor ) { tabBar . rippleColor = colorScheme . primaryColor . colorWithAlphaComponent ( 0.24 ) ; tabBar . tintColor = colorScheme . primaryColor ; tabBar . selectionIndicatorStrokeColor = colorScheme . primaryColor ; owner . setTabBarColor ( colorScheme . primaryColor ) ; } else { if ( majorVersion <= 12 || ! UIColor . labelColor ) { tabBar . tintColor = UIColor . blueColor ; tabBar . barTintColor = UIColor . whiteColor ; tabBar . setTitleColorForState ( UIColor . blackColor , UIControlState . Normal ) ; tabBar . setTitleColorForState ( UIColor . blackColor , UIControlState . Selected ) ; } else { tabBar . tintColor = UIColor . systemBlueColor ; tabBar . barTintColor = UIColor . systemBackgroundColor ; tabBar . setTitleColorForState ( UIColor . labelColor , UIControlState . Normal ) ; tabBar . setTitleColorForState ( UIColor . labelColor , UIControlState . Selected ) ; tabBar . rippleColor = UIColor . clearColor ; } } tabBar . autoresizingMask = UIViewAutoresizing . FlexibleWidth | UIViewAutoresizing . FlexibleBottomMargin ; this . view . addSubview ( tabBar ) ; tabBar . sizeToFit ( ) ; } public viewWillAppear ( animated ) { super . viewWillAppear ( animated ) ; const owner = this . _owner . get ( ) ; if ( ! owner ) { return ; } IOSHelper . updateAutoAdjustScrollInsets ( this , owner ) ; if ( ! owner . isLoaded ) { owner . callLoaded ( ) ; } } public viewDidLayoutSubviews ( ) { super . viewDidLayoutSubviews ( ) ; const owner = this . _owner . get ( ) ; if ( ! owner ) { return ; } let safeAreaInsetsBottom = 0 ; let safeAreaInsetsTop = 0 ; if ( majorVersion > 10 ) { safeAreaInsetsBottom = this . view . safeAreaInsets . bottom ; safeAreaInsetsTop = this . view . safeAreaInsets . top ; } else { safeAreaInsetsTop = this . topLayoutGuide . length ; } const conditionalSafeAreaBottom = owner . iosOverflowSafeArea ? safeAreaInsetsBottom : 0 ; let scrollViewTop = 0 ; let scrollViewHeight = this . view . bounds . size . height + conditionalSafeAreaBottom ; if ( owner . tabStrip && this . tabBar ) { const viewWidth = this . view . bounds . size . width ; const viewHeight = this . view . bounds . size . height ; const tabBarHeight = this . tabBar . frame . size . height ; let tabBarTop = safeAreaInsetsTop ; scrollViewTop = tabBarHeight ; scrollViewHeight = this . view . bounds . size . height - tabBarHeight + conditionalSafeAreaBottom ; const tabsPosition = owner . tabsPosition ; if ( tabsPosition === 'bottom' ) { tabBarTop = viewHeight - tabBarHeight - safeAreaInsetsBottom ; scrollViewTop = this . view . frame . origin . y ; scrollViewHeight = viewHeight - tabBarHeight ; } let parent = owner . parent ; while ( parent && ! parent . nativeViewProtected ) { parent = parent . parent ; } if ( parent && majorVersion > 10 ) { tabBarTop = Math . max ( tabBarTop , parent . nativeView . safeAreaInsets . top ) ; } this . tabBar . frame = CGRectMake ( 0 , tabBarTop , viewWidth , tabBarHeight ) ; } else { this . tabBar . hidden = true ; } const subViews = this . view . subviews ; let scrollView = null ; for ( let i = 0 ; i < subViews . count ; i ++ ) { const view = subViews [ i ] ; if ( view instanceof UIScrollView && ! ( view instanceof MDCTabBarView ) ) { scrollView = view ; } } if ( scrollView ) { this . scrollView = scrollView ; if ( ! owner . swipeEnabled ) { scrollView . scrollEnabled = false ; } else { scrollView . scrollEnabled = true ; } scrollView . frame = CGRectMake ( 0 , scrollViewTop , this . view . bounds . size . width , scrollViewHeight ) ; } } public traitCollectionDidChange ( previousTraitCollection ) { super . traitCollectionDidChange ( previousTraitCollection ) ; if ( majorVersion >= 13 ) { const owner = this . _owner . get ( ) ; if ( owner && this . traitCollection . hasDifferentColorAppearanceComparedToTraitCollection && this . traitCollection . hasDifferentColorAppearanceComparedToTraitCollection ( previousTraitCollection ) ) { owner . notify ( { eventName : IOSHelper . traitCollectionColorAppearanceChangedEvent , object : owner } ) ; } } } public viewWillTransitionToSizeWithTransitionCoordinator ( size , coordinator ) { super . viewWillTransitionToSizeWithTransitionCoordinator ( size , coordinator ) ; coordinator . animateAlongsideTransitionCompletion ( ( ) => { const owner = this . _owner . get ( ) ; if ( owner && owner . tabStrip && owner . tabStrip . items ) { const tabStrip = owner . tabStrip ; tabStrip . items . forEach ( ( tabStripItem ) => { updateBackgroundPositions ( tabStrip , tabStripItem , owner . selectedIndex !== tabStripItem . _index ? owner . _defaultItemBackgroundColor : null ) ; const index = tabStripItem . _index ; const tabBarItemController = owner . viewControllers [ index ] ; updateTitleAndIconPositions ( tabStripItem , tabBarItemController . tabBarItem , tabBarItemController ) ; } ) ; } } , null ) ; } } @ NativeClass class UIPageViewControllerDataSourceImpl extends NSObject implements UIPageViewControllerDataSource { public static ObjCProtocols = [ UIPageViewControllerDataSource ] ; private _owner : WeakRef < Tabs > ; public static initWithOwner ( owner ) { const dataSource = UIPageViewControllerDataSourceImpl . new ( ) as UIPageViewControllerDataSourceImpl ; dataSource . _owner = owner ; return dataSource ; } public pageViewControllerViewControllerBeforeViewController ( pageViewController , viewController ) { const owner = this . _owner . get ( ) ; let selectedIndex = owner . selectedIndex ; if ( selectedIndex === 0 ) { return null ; } selectedIndex -- ; const prevItem = owner . items [ selectedIndex ] ; const prevViewController = ( prevItem as any ) . __controller ; owner . _setCanBeLoaded ( selectedIndex ) ; owner . _loadUnloadTabItems ( selectedIndex ) ; return prevViewController ; } public pageViewControllerViewControllerAfterViewController ( pageViewController , viewController ) { const owner = this . _owner . get ( ) ; let selectedIndex = owner . selectedIndex ; if ( selectedIndex === owner . items . length - 1 ) { return null ; } selectedIndex ++ ; const nextItem = owner . items [ selectedIndex ] ; const nextViewController = ( nextItem as any ) . __controller ; owner . _setCanBeLoaded ( selectedIndex ) ; owner . _loadUnloadTabItems ( selectedIndex ) ; return nextViewController ; } public presentationCountForPageViewController ( pageViewController ) { return 0 ; } public presentationIndexForPageViewController ( pageViewController ) { return 0 ; } } @ NativeClass class UIPageViewControllerDelegateImpl extends NSObject implements UIPageViewControllerDelegate { public static ObjCProtocols = [ UIPageViewControllerDelegate ] ; private _owner : WeakRef < Tabs > ; public static initWithOwner ( owner ) { const delegate = UIPageViewControllerDelegateImpl . new ( ) as UIPageViewControllerDelegateImpl ; delegate . _owner = owner ; return delegate ; } public pageViewControllerWillTransitionToViewControllers ( pageViewController , viewControllers ) { } public pageViewControllerDidFinishAnimatingPreviousViewControllersTransitionCompleted ( pageViewController , didFinishAnimating , previousViewControllers , transitionCompleted ) { if ( ! transitionCompleted ) { return ; } const owner = this . _owner . get ( ) ; const ownerViewControllers = owner . viewControllers ; const selectedIndex = owner . selectedIndex ; const nextViewController = pageViewController . viewControllers [ 0 ] ; const nextViewControllerIndex = ownerViewControllers . indexOf ( nextViewController ) ; if ( selectedIndex !== nextViewControllerIndex ) { owner . _animateNextChange = false ; owner . selectedIndex = nextViewControllerIndex ; owner . _canSelectItem = true ; } if ( owner . _needsCacheUpdate ) { invokeOnRunLoop ( ( ) => { owner . _needsCacheUpdate = false ; const viewController = owner . viewController ; viewController . dataSource = null ; viewController . dataSource = ( owner as any ) . _dataSource ; } ) ; } } } function iterateIndexRange ( index , eps , lastIndex , callback ) { const rangeStart = Math . max ( 0 , index - eps ) ; const rangeEnd = Math . min ( index + eps , lastIndex ) ; for ( let i = rangeStart ; i <= rangeEnd ; i ++ ) { callback ( i ) ; } } function updateBackgroundPositions ( tabStrip , tabStripItem , color = null ) { if ( ! tabStrip . nativeView || tabStripItem . _index === undefined ) { return ; } let bgView = ( tabStripItem as any ) . bgView ; const index = tabStripItem . _index ; const width = tabStrip . nativeView . frame . size . width / tabStrip . items . filter ( ( s ) => s . _index !== undefined ) . length ; const frame = CGRectMake ( width * index , 0 , width , tabStrip . nativeView . frame . size . width ) ; if ( ! bgView ) { bgView = UIView . alloc ( ) . initWithFrame ( frame ) ; tabStrip . nativeView . insertSubviewAtIndex ( bgView , 0 ) ; ( tabStripItem as any ) . bgView = bgView ; } else { bgView . frame = frame ; } const backgroundColor = tabStripItem . style . backgroundColor ; bgView . backgroundColor = color || ( backgroundColor instanceof Color ? backgroundColor . ios : backgroundColor ) ; } function updateTitleAndIconPositions ( tabStripItem , tabBarItem , controller ) { if ( ! tabStripItem || ! tabBarItem ) { return ; } const orientation = controller . interfaceOrientation ; const isPortrait = orientation !== UIInterfaceOrientation . LandscapeLeft && orientation !== UIInterfaceOrientation . LandscapeRight ; const isIconAboveTitle = majorVersion < 11 || ( isPhone && isPortrait ) ; if ( ! tabStripItem . iconSource ) { if ( isIconAboveTitle ) { tabBarItem . titlePositionAdjustment = { horizontal : 0 , vertical : - 20 } ; } else { tabBarItem . titlePositionAdjustment = { horizontal : 0 , vertical : 0 } ; } } if ( ! tabStripItem . title ) { if ( isIconAboveTitle ) { tabBarItem . imageInsets = new UIEdgeInsets ( { top : 6 , left : 0 , bottom : - 6 , right : 0 } ) ; } else { tabBarItem . imageInsets = new UIEdgeInsets ( { top : 0 , left : 0 , bottom : 0 , right : 0 } ) ; } } } export const iosCustomPositioningProperty = new Property < Tabs , boolean > ( { name : 'iosCustomPositioning' , defaultValue : false , valueConverter : booleanConverter } ) ; export class Tabs extends TabsBase { public nativeViewProtected : UIView ; public selectedIndex : number ; public _canSelectItem : boolean ; public isLoaded : boolean ; public viewController : UIPageViewControllerImpl ; public items : TabContentItem [ ] ; public _ios : UIPageViewControllerImpl ; public viewControllers : UIViewController [ ] ; public tabBarItems : UITabBarItem [ ] ; private _currentNativeSelectedIndex : number ; private _dataSource : UIPageViewControllerDataSourceImpl ; private _delegate : UIPageViewControllerDelegateImpl ; private _iconsCache = { } ; private _backgroundIndicatorColor : UIColor ; public _defaultItemBackgroundColor : UIColor ; private _selectedItemColor : Color ; private _unSelectedItemColor : Color ; public animationEnabled : boolean ; public _needsCacheUpdate = false ; public _animateNextChange = true ; private _selectionIndicatorColor : Color ; private _rippleColor : Color ; public iosCustomPositioning : boolean ; constructor ( ) { super ( ) ; this . viewController = this . _ios = UIPageViewControllerImpl . initWithOwner ( new WeakRef ( this ) ) ; } createNativeView ( ) { return this . _ios . view ; } initNativeView ( ) { super . initNativeView ( ) ; this . _dataSource = UIPageViewControllerDataSourceImpl . initWithOwner ( new WeakRef ( this ) ) ; this . _delegate = UIPageViewControllerDelegateImpl . initWithOwner ( new WeakRef ( this ) ) ; } disposeNativeView ( ) { this . _dataSource = null ; this . _delegate = null ; this . _ios . tabBarDelegate = null ; this . _ios . tabBar = null ; super . disposeNativeView ( ) ; } public onLoaded ( ) { super . onLoaded ( ) ; this . setViewControllers ( this . items ) ; const selectedIndex = this . selectedIndex ; const selectedView = this . items && this . items [ selectedIndex ] && this . items [ selectedIndex ] . content ; if ( selectedView instanceof Frame ) { selectedView . _pushInFrameStackRecursive ( ) ; } this . _ios . dataSource = this . _dataSource ; this . _ios . delegate = this . _delegate ; } public onUnloaded ( ) { this . _ios . dataSource = null ; this . _ios . delegate = null ; super . onUnloaded ( ) ; } get ios ( ) : UIPageViewController { return this . _ios ; } public layoutNativeView ( left , top , right , bottom ) { if ( this . iosCustomPositioning ) { super . layoutNativeView ( left , top , right , bottom ) ; } } public _setNativeViewFrame ( nativeView , frame ) { if ( this . iosCustomPositioning ) { super . _setNativeViewFrame ( nativeView , frame ) ; } } public onSelectedIndexChanged ( oldIndex , newIndex ) { const items = this . items ; if ( ! items ) { return ; } const oldItem = items [ oldIndex ] ; if ( oldItem ) { oldItem . canBeLoaded = false ; oldItem . unloadView ( oldItem . content ) ; } const newItem = items [ newIndex ] ; if ( newItem && this . isLoaded ) { const selectedView = items [ newIndex ] . content ; if ( selectedView instanceof Frame ) { selectedView . _pushInFrameStackRecursive ( ) ; } newItem . canBeLoaded = true ; newItem . loadView ( newItem . content ) ; } const tabStripItems = this . tabStrip && this . tabStrip . items ; if ( tabStripItems ) { if ( tabStripItems [ newIndex ] ) { tabStripItems [ newIndex ] . _emit ( TabStripItem . selectEvent ) ; this . updateItemColors ( tabStripItems [ newIndex ] ) ; } if ( tabStripItems [ oldIndex ] ) { tabStripItems [ oldIndex ] . _emit ( TabStripItem . unselectEvent ) ; this . updateItemColors ( tabStripItems [ oldIndex ] ) ; } } this . _loadUnloadTabItems ( newIndex ) ; super . onSelectedIndexChanged ( oldIndex , newIndex ) ; } public _loadUnloadTabItems ( newIndex ) { const items = this . items ; if ( ! items ) { return ; } const lastIndex = items . length - 1 ; const offsideItems = this . offscreenTabLimit ; const toUnload = [ ] ; const toLoad = [ ] ; iterateIndexRange ( newIndex , offsideItems , lastIndex , ( i ) => toLoad . push ( i ) ) ; if ( this . unloadOnTabChange ) { items . forEach ( ( item , i ) => { const indexOfI = toLoad . indexOf ( i ) ; if ( indexOfI < 0 ) { toUnload . push ( i ) ; } } ) ; toUnload . forEach ( ( index ) => { const item = items [ index ] ; if ( items [ index ] ) { item . unloadView ( item . content ) ; } } ) ; } const newItem = items [ newIndex ] ; const selectedView = newItem && newItem . content ; if ( selectedView instanceof Frame ) { selectedView . _pushInFrameStackRecursive ( ) ; } toLoad . forEach ( ( index ) => { const item = items [ index ] ; if ( this . isLoaded && items [ index ] ) { item . loadView ( item . content ) ; } } ) ; } public onMeasure ( widthMeasureSpec , heightMeasureSpec ) { const width = Utils . layout . getMeasureSpecSize ( widthMeasureSpec ) ; const widthMode = Utils . layout . getMeasureSpecMode ( widthMeasureSpec ) ; const height = Utils . layout . getMeasureSpecSize ( heightMeasureSpec ) ; const heightMode = Utils . layout . getMeasureSpecMode ( heightMeasureSpec ) ; const widthAndState = View . resolveSizeAndState ( width , width , widthMode , 0 ) ; const heightAndState = View . resolveSizeAndState ( height , height , heightMode , 0 ) ; this . setMeasuredDimension ( widthAndState , heightAndState ) ; } public _onViewControllerShown ( viewController ) { if ( this . _ios . viewControllers && this . _ios . viewControllers . containsObject ( viewController ) ) { this . selectedIndex = this . _ios . viewControllers . indexOfObject ( viewController ) ; } else { } } private getViewController ( item ) { let newController = item . content ? item . content . viewController : null ; if ( newController ) { ( item as any ) . setViewController ( newController , newController . view ) ; return newController ; } if ( item . content . ios instanceof UIViewController ) { newController = item . content . ios ; ( item as any ) . setViewController ( newController , newController . view ) ; } else if ( item . content . ios && item . content . ios . controller instanceof UIViewController ) { newController = item . content . ios . controller ; ( item as any ) . setViewController ( newController , newController . view ) ; } else { newController = IOSHelper . UILayoutViewController . initWithOwner ( new WeakRef ( item . content ) ) as UIViewController ; newController . view . addSubview ( item . content . nativeViewProtected ) ; item . content . viewController = newController ; ( item as any ) . setViewController ( newController , item . content . nativeViewProtected ) ; } return newController ; } public _setCanBeLoaded ( index ) { const items = this . items ; if ( ! this . items ) { return ; } const lastIndex = items . length - 1 ; const offsideItems = this . offscreenTabLimit ; iterateIndexRange ( index , offsideItems , lastIndex , ( i ) => { if ( items [ i ] ) { items [ i ] . canBeLoaded = true ; } } ) ; } private setViewControllers ( items ) { const length = items ? items . length : 0 ; if ( length === 0 ) { this . viewControllers = null ; return ; } const viewControllers = [ ] ; const tabBarItems = [ ] ; if ( this . tabStrip ) { this . tabStrip . setNativeView ( this . _ios . tabBar ) ; } const tabStripItems = this . tabStrip && this . tabStrip . items ; if ( tabStripItems ) { if ( tabStripItems [ this . selectedIndex ] ) { tabStripItems [ this . selectedIndex ] . _emit ( TabStripItem . selectEvent ) ; } } items . forEach ( ( item , i ) => { const controller = this . getViewController ( item ) ; if ( this . tabStrip && this . tabStrip . items && this . tabStrip . items [ i ] ) { const tabStripItem = this . tabStrip . items [ i ] ; const tabBarItem = this . createTabBarItem ( tabStripItem , i ) ; updateTitleAndIconPositions ( tabStripItem , tabBarItem , controller ) ; this . setViewTextAttributes ( tabStripItem . label , i === this . selectedIndex ) ; controller . tabBarItem = tabBarItem ; tabStripItem . _index = i ; tabBarItems . push ( tabBarItem ) ; tabStripItem . setNativeView ( tabBarItem ) ; } item . canBeLoaded = true ; viewControllers . push ( controller ) ; } ) ; this . setItemImages ( ) ; this . viewControllers = viewControllers ; this . tabBarItems = tabBarItems ; if ( this . viewController && this . viewController . tabBar ) { this . viewController . tabBar . items = NSArray . arrayWithArray ( this . tabBarItems ) ; this . viewController . tabBar . sizeToFit ( ) ; this . viewController . tabBar . setSelectedItemAnimated ( this . tabBarItems [ this . selectedIndex ] , false ) ; } } public onItemsChanged ( oldItems , newItems ) { this . _needsCacheUpdate = true ; super . onItemsChanged ( oldItems , newItems ) ; if ( oldItems ) { this . _canSelectItem = true ; this . _setCanBeLoaded ( this . selectedIndex ) ; this . _loadUnloadTabItems ( this . selectedIndex ) ; } } private setItemImages ( ) { if ( this . _selectedItemColor || this . _unSelectedItemColor ) { if ( this . tabStrip && this . tabStrip . items ) { this . tabStrip . items . forEach ( ( item ) => { if ( this . _unSelectedItemColor && item . nativeView ) { item . nativeView . image = this . getIcon ( item , this . _unSelectedItemColor ) ; } if ( this . _selectedItemColor && item . nativeView ) { if ( this . selectedIndex === item . _index ) { item . nativeView . image = this . getIcon ( item , this . _selectedItemColor ) ; } } } ) ; } } } private updateAllItemsColors ( ) { this . _defaultItemBackgroundColor = null ; this . setItemColors ( ) ; if ( this . tabStrip && this . tabStrip . items ) { this . tabStrip . items . forEach ( ( tabStripItem ) => { this . updateItemColors ( tabStripItem ) ; } ) ; } } private updateItemColors ( tabStripItem ) { updateBackgroundPositions ( this . tabStrip , tabStripItem ) ; this . setIconColor ( tabStripItem , true ) ; } private createTabBarItem ( item , index ) { let image ; let title ; if ( item . isLoaded ) { image = this . getIcon ( item ) ; title = item . label . text ; 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 getIconRenderingMode ( ) { switch ( this . tabStrip && this . tabStrip . iosIconRenderingMode ) { case 'alwaysOriginal' : return UIImageRenderingMode . AlwaysOriginal ; case 'alwaysTemplate' : return UIImageRenderingMode . AlwaysTemplate ; case 'automatic' : default : const hasItemColor = this . _selectedItemColor || this . _unSelectedItemColor ; return hasItemColor ? UIImageRenderingMode . AlwaysTemplate : UIImageRenderingMode . AlwaysOriginal ; } } private getIcon ( tabStripItem , color ? ) { const iconSource = tabStripItem . image && tabStripItem . image . src ; if ( ! iconSource ) { return null ; } const target = tabStripItem . image ; const font = target . style . fontInternal || Font . default ; if ( ! color ) { color = target . style . color ; } const iconTag = [ iconSource , font . fontStyle , font . fontWeight , font . fontSize , font . fontFamily , color ] . join ( ';' ) ; let isFontIcon = false ; let image = this . _iconsCache [ iconTag ] ; if ( ! image ) { let is ; if ( Utils . isFontIconURI ( iconSource ) ) { isFontIcon = true ; const fontIconCode = iconSource . split ( '//' ) [ 1 ] ; is = ImageSource . fromFontIconCodeSync ( fontIconCode , font , color ) ; } else { is = ImageSource . fromFileOrResourceSync ( iconSource ) ; } if ( is && is . ios ) { image = is . ios ; if ( this . tabStrip && this . tabStrip . isIconSizeFixed ) { image = this . getFixedSizeIcon ( image ) ; } let renderingMode = UIImageRenderingMode . Automatic ; if ( ! isFontIcon ) { renderingMode = this . getIconRenderingMode ( ) ; } const originalRenderedImage = image . imageWithRenderingMode ( renderingMode ) ; this . _iconsCache [ iconTag ] = originalRenderedImage ; image = originalRenderedImage ; } } return image ; } private getFixedSizeIcon ( image ) { const inWidth = image . size . width ; const inHeight = image . size . height ; const iconSpecSize = getIconSpecSize ( { width : inWidth , height : inHeight } ) ; const widthPts = iconSpecSize . width ; const heightPts = iconSpecSize . height ; UIGraphicsBeginImageContextWithOptions ( { width : widthPts , height : heightPts } , false , Utils . layout . getDisplayDensity ( ) ) ; image . drawInRect ( CGRectMake ( 0 , 0 , widthPts , heightPts ) ) ; const resultImage = UIGraphicsGetImageFromCurrentImageContext ( ) ; UIGraphicsEndImageContext ( ) ; return resultImage ; } public getTabBarBackgroundColor ( ) { return this . _ios . tabBar . barTintColor ; } public setTabBarBackgroundColor ( value ) { this . _ios . tabBar . barTintColor = value instanceof Color ? value . ios : value ; this . updateAllItemsColors ( ) ; } public setTabBarItemTitle ( tabStripItem , value ) { const nativeView = tabStripItem . nativeView ; if ( ! nativeView ) { return ; } nativeView . title = value ; } private equalUIColor ( first , second ) { if ( ! first && ! second ) { return true ; } if ( ! first || ! second ) { return false ; } const firstComponents = CGColorGetComponents ( first . CGColor ) ; const secondComponents = CGColorGetComponents ( second . CGColor ) ; return firstComponents [ 0 ] === secondComponents [ 0 ] && firstComponents [ 1 ] === secondComponents [ 1 ] && firstComponents [ 2 ] === secondComponents [ 2 ] && firstComponents [ 3 ] === secondComponents [ 3 ] ; } private isSelectedAndHightlightedItem ( tabStripItem ) { return tabStripItem . _index === this . selectedIndex && tabStripItem [ '_visualState' ] === 'highlighted' ; } public setTabBarItemBackgroundColor ( tabStripItem , value ) { if ( ! this . tabStrip || ! tabStripItem || ! tabStripItem . nativeView ) { return ; } const newColor = value instanceof Color ? value . ios : value ; const itemSelectedAndHighlighted = this . isSelectedAndHightlightedItem ( tabStripItem ) ; if ( ! this . _defaultItemBackgroundColor && ! itemSelectedAndHighlighted ) { this . _defaultItemBackgroundColor = newColor ; } if ( itemSelectedAndHighlighted && ! this . equalUIColor ( this . _defaultItemBackgroundColor , newColor ) ) { if ( ! this . _backgroundIndicatorColor ) { this . _backgroundIndicatorColor = newColor ; this . _ios . tabBar . selectionIndicatorTemplate = new BackgroundIndicatorTemplate ( ) ; this . _ios . tabBar . tintColor = newColor ; } } else { updateBackgroundPositions ( this . tabStrip , tabStripItem , newColor ) ; } } public setTabBarItemColor ( tabStripItem , value ) { this . setViewTextAttributes ( tabStripItem . label ) ; } private setItemColors ( ) { if ( this . _selectedItemColor ) { this . viewController . tabBar . setTitleColorForState ( this . _selectedItemColor . ios , UIControlState . Selected ) ; } if ( this . _unSelectedItemColor ) { this . viewController . tabBar . setTitleColorForState ( this . _unSelectedItemColor . ios , UIControlState . Normal ) ; } } private setIconColor ( tabStripItem , forceReload = false ) { const nativeView = tabStripItem . nativeView ; if ( ! nativeView ) { return ; } if ( ! forceReload && ! this . _selectedItemColor && ! this . _unSelectedItemColor ) { return ; } if ( this . _selectedItemColor ) { const image = this . getIcon ( tabStripItem , this . _selectedItemColor ) ; nativeView . selectedImage = image ; } if ( this . _unSelectedItemColor ) { const image = this . getIcon ( tabStripItem , this . _unSelectedItemColor ) ; nativeView . image = image ; } } public setTabBarIconColor ( tabStripItem , value ) { this . setIconColor ( tabStripItem , true ) ; } public setTabBarIconSource ( tabStripItem , value ) { this . updateItemColors ( tabStripItem ) ; } public setTabBarItemFontInternal ( tabStripItem , value ) { this . setViewTextAttributes ( tabStripItem . label ) ; } public getTabBarFontInternal ( ) { return this . _ios . tabBar . titleFontForState ( UIControlState . Normal ) ; } public setTabBarFontInternal ( value ) { const defaultTabItemFontSize = 10 ; const tabItemFontSize = this . tabStrip . style . fontSize || defaultTabItemFontSize ; const font = ( this . tabStrip . style . fontInternal || Font . default ) . getUIFont ( UIFont . systemFontOfSize ( tabItemFontSize ) ) ; this . _ios . tabBar . setTitleFontForState ( font , UIControlState . Normal ) ; this . _ios . tabBar . setTitleFontForState ( font , UIControlState . Selected ) ; } public getTabBarColor ( ) { return this . _ios . tabBar . titleColorForState ( UIControlState . Normal ) ; } public setTabBarColor ( value ) { const nativeColor = value instanceof Color ? value . ios : value ; this . _ios . tabBar . setTitleColorForState ( nativeColor , UIControlState . Selected ) ; this . _ios . tabBar . setImageTintColorForState ( nativeColor , UIControlState . Selected ) ; } public getTabBarHighlightColor ( ) { return this . _selectionIndicatorColor ; } public setTabBarHighlightColor ( value ) { this . _selectionIndicatorColor = value ; this . _ios . tabBar . selectionIndicatorStrokeColor = value . ios ; } public getTabBarSelectedItemColor ( ) { return this . _selectedItemColor ; } public setTabBarSelectedItemColor ( value ) { this . _selectedItemColor = value ; this . updateAllItemsColors ( ) ; } public getTabBarUnSelectedItemColor ( ) { return this . _unSelectedItemColor ; } public setTabBarUnSelectedItemColor ( value ) { this . _unSelectedItemColor = value ; this . updateAllItemsColors ( ) ; } private visitFrames ( view , operation ) { if ( view instanceof Frame ) { operation ( view ) ; } view . eachChild ( ( child ) => { this . visitFrames ( child , operation ) ; return true ; } ) ; } public setTabBarRippleColor ( value ) { this . _rippleColor = value ; this . _ios . tabBar . rippleColor = value . ios ; } public getTabBarRippleColor ( ) { return this . _rippleColor ; } [ selectedIndexProperty . setNative ] ( value ) { if ( value > - 1 ) { const item = this . items [ value ] ; const controllers = NSMutableArray . alloc < UIViewController > ( ) . initWithCapacity ( 1 ) ; const itemController = ( item as any ) . __controller ; controllers . addObject ( itemController ) ; let navigationDirection = UIPageViewControllerNavigationDirection . Forward ; if ( this . _currentNativeSelectedIndex && this . _currentNativeSelectedIndex > value ) { navigationDirection = UIPageViewControllerNavigationDirection . Reverse ; } this . _currentNativeSelectedIndex = value ; this . visitFrames ( item , ( frame ) => ( frame . _animationInProgress = true ) ) ; const doneAnimating = ( ) => { this . visitFrames ( item , ( frame ) => ( frame . _animationInProgress = false ) ) ; this . _canSelectItem = true ; this . _setCanBeLoaded ( value ) ; this . _loadUnloadTabItems ( value ) ; } ; if ( this . _animateNextChange ) { invokeOnRunLoop ( ( ) => { this . viewController . setViewControllersDirectionAnimatedCompletion ( controllers , navigationDirection , this . animationEnabled , ( finished ) => { if ( finished ) { if ( this . animationEnabled ) { invokeOnRunLoop ( ( ) => { doneAnimating ( ) ; } ) ; } else { doneAnimating ( ) ; } } } ) ; } ) ; } else { this . _animateNextChange = true ; doneAnimating ( ) ; } if ( this . tabBarItems && this . tabBarItems . length && this . viewController && this . viewController . tabBar ) { this . viewController . tabBar . setSelectedItemAnimated ( this . tabBarItems [ value ] , this . animationEnabled ) ; } } } [ itemsProperty . getDefault ] ( ) { return null ; } [ itemsProperty . setNative ] ( value ) { if ( value ) { value . forEach ( ( item , i ) => { ( item as any ) . index = i ; } ) ; } this . setViewControllers ( value ) ; selectedIndexProperty . coerce ( this ) ; } [ tabStripProperty . getDefault ] ( ) { return null ; } [ tabStripProperty . setNative ] ( value ) { this . setViewControllers ( this . items ) ; selectedIndexProperty . coerce ( this ) ; } [ swipeEnabledProperty . getDefault ] ( ) { return true ; } [ swipeEnabledProperty . setNative ] ( value ) { if ( this . viewController && this . viewController . scrollView ) { this . viewController . scrollView . scrollEnabled = value ; } } private setViewTextAttributes ( view , setSelected = false ) { if ( ! view ) { return null ; } const defaultTabItemFontSize = 10 ; const tabItemFontSize = view . style . fontSize || defaultTabItemFontSize ; const font = ( view . style . fontInternal || Font . default ) . getUIFont ( UIFont . systemFontOfSize ( tabItemFontSize ) ) ; this . viewController . tabBar . setTitleFontForState ( font , UIControlState . Normal ) ; this . viewController . tabBar . setTitleFontForState ( font , UIControlState . Selected ) ; const tabItemTextColor = view . style . color ; const textColor = tabItemTextColor instanceof Color ? tabItemTextColor . ios : null ; if ( textColor ) { this . viewController . tabBar . setTitleColorForState ( textColor , UIControlState . Normal ) ; this . viewController . tabBar . setImageTintColorForState ( textColor , UIControlState . Normal ) ; if ( setSelected ) { this . viewController . tabBar . setTitleColorForState ( textColor , UIControlState . Selected ) ; this . viewController . tabBar . setImageTintColorForState ( textColor , UIControlState . Selected ) ; } } if ( this . _selectedItemColor ) { this . viewController . tabBar . setTitleColorForState ( this . _selectedItemColor . ios , UIControlState . Selected ) ; this . viewController . tabBar . setImageTintColorForState ( this . _selectedItemColor . ios , UIControlState . Selected ) ; } if ( this . _unSelectedItemColor ) { this . viewController . tabBar . setTitleColorForState ( this . _unSelectedItemColor . ios , UIControlState . Normal ) ; this . viewController . tabBar . setImageTintColorForState ( this . _unSelectedItemColor . ios , UIControlState . Selected ) ; } } } iosCustomPositioningProperty . register ( Tabs ) ;