From 943d2dcd6ba077b30fbbb04a3b40bf23e2613a84 Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Mon, 5 Dec 2016 18:12:25 +0200 Subject: [PATCH] tabview-ios --- tns-core-modules/ui/core/properties.ts | 201 +++++++++++--- tns-core-modules/ui/definitions.d.ts | 12 + .../ui/tab-view/tab-view-common.ts | 45 ++- .../ui/tab-view/tab-view.android.ts | 78 +++--- tns-core-modules/ui/tab-view/tab-view.ios.ts | 259 +++++++++--------- tns-core-modules/utils/utils.ios.ts | 6 +- 6 files changed, 365 insertions(+), 236 deletions(-) diff --git a/tns-core-modules/ui/core/properties.ts b/tns-core-modules/ui/core/properties.ts index 05f1cf7ad..29ba18bb1 100644 --- a/tns-core-modules/ui/core/properties.ts +++ b/tns-core-modules/ui/core/properties.ts @@ -26,6 +26,10 @@ export interface PropertyOptions { valueConverter?: (value: any) => U } +export interface CoerciblePropertyOptions extends PropertyOptions { + coerceValue(T, U): U +} + export interface ShorthandPropertyOptions { name: string, cssName: string; @@ -40,9 +44,9 @@ export interface CssPropertyOptions extends PropertyOptions< export class Property implements PropertyDescriptor { private registered: boolean; - private name: string; - public key: symbol; - public native: symbol; + private readonly name: string; + public readonly key: symbol; + public readonly native: symbol; public get: () => U; public set: (value: U) => void; @@ -67,22 +71,137 @@ export class Property implements PropertyDescriptor { let valueConverter = options.valueConverter; this.set = function (this: T, value: any): void { - if (value === unsetValue) { - value = defaultValue; - } + let reset = value === unsetValue; + let unboxedValue: U; + let wrapped: boolean; + if (reset) { + unboxedValue = defaultValue; + } else { + wrapped = value && (value).wrapped; + unboxedValue = wrapped ? WrappedValue.unwrap(value) : value; - let wrapped = value && value.wrapped; - let unboxedValue: U = wrapped ? WrappedValue.unwrap(value) : value; - - if (valueConverter) { - unboxedValue = valueConverter(unboxedValue); + if (valueConverter && typeof unboxedValue === "string") { + unboxedValue = valueConverter(unboxedValue); + } } let currentValue = key in this ? this[key] : defaultValue; let changed: boolean = equalityComparer ? !equalityComparer(currentValue, unboxedValue) : currentValue !== unboxedValue; if (wrapped || changed) { - this[key] = unboxedValue; + if (reset) { + delete this[key]; + } else { + this[key] = unboxedValue; + } + + if (valueChanged) { + valueChanged(this, currentValue, unboxedValue); + } + + if (this.hasListeners(eventName)) { + this.notify({ + eventName: eventName, + propertyName: name, + object: this, + value: unboxedValue + }); + } + + let nativeObject = this.nativeView; + if (nativeObject) { + this[native] = unboxedValue; + } + + if (affectsLayout) { + this.requestLayout(); + } + } + } + + this.get = function (): U { + return key in this ? this[key] : defaultValue; + } + + symbolPropertyMap[key] = this; + } + + public register(cls: { prototype: T }): void { + if (this.registered) { + throw new Error(`Property ${this.name} already registered.`); + } + this.registered = true; + Object.defineProperty(cls.prototype, this.name, this); + } +} + +export class CoercibleProperty implements PropertyDescriptor { + private registered: boolean; + private readonly name: string; + public readonly key: symbol; + public readonly native: symbol; + + public get: () => U; + public set: (value: U) => void; + public enumerable: boolean = true; + public configurable: boolean = true; + + public readonly coerce: (target: T) => void; + + constructor(options: CoerciblePropertyOptions) { + let name = options.name; + this.name = name; + + let key = Symbol(name + ":propertyKey"); + this.key = key; + + let native: symbol = Symbol(name + ":nativeKey"); + this.native = native; + + let coerceKey = Symbol(name + ":coerceKey"); + + let eventName = name + "Change"; + let defaultValue: U = options.defaultValue; + let affectsLayout: boolean = options.affectsLayout; + let equalityComparer = options.equalityComparer; + let valueChanged = options.valueChanged; + let valueConverter = options.valueConverter; + let coerceCallback = options.coerceValue; + + this.coerce = function (target: T): void { + let originalValue: U = coerceKey in target ? target[coerceKey] : defaultValue; + target[key] = originalValue; + } + + this.set = function (this: T, value: U): void { + let reset = value === unsetValue; + let unboxedValue: U; + let wrapped: boolean; + if (reset) { + unboxedValue = defaultValue; + delete this[coerceKey]; + } else { + wrapped = value && (value).wrapped; + unboxedValue = wrapped ? WrappedValue.unwrap(value) : value; + + if (valueConverter && typeof unboxedValue === "string") { + unboxedValue = valueConverter(unboxedValue); + } + + this[coerceKey] = unboxedValue; + unboxedValue = coerceCallback(this, unboxedValue); + } + + let currentValue = key in this ? this[key] : defaultValue; + let changed: boolean = equalityComparer ? !equalityComparer(currentValue, unboxedValue) : currentValue !== unboxedValue; + + if (wrapped || changed) { + if (reset) { + delete this[key]; + } else { + this[key] = unboxedValue; + } + if (valueChanged) { valueChanged(this, currentValue, unboxedValue); } @@ -170,16 +289,16 @@ export class InheritedProperty extends Property { that[sourceKey] = newValueSource; if (currentValue !== newValue) { - let resetValue = newValueSource === ValueSource.Default; + let reset = newValueSource === ValueSource.Default; that.eachChild((child) => { let childValueSource = child[sourceKey]; - if (resetValue) { + if (reset) { if (childValueSource === ValueSource.Inherited) { setFunc.call(child, unsetValue); } } else { if (childValueSource <= ValueSource.Inherited) { - setInheritedValue.call(child, unboxedValue); + setInheritedValue.call(child, child.parent[key]); } } return true; @@ -239,13 +358,14 @@ export class CssProperty { let dependentPropertyNativeKey = dependentProperty ? dependentProperty.native : undefined; function setLocalValue(this: T, value: U): void { - if (value === unsetValue) { + let reset = value === unsetValue; + if (reset) { value = defaultValue; - this[sourceKey] = ValueSource.Default; + delete this[sourceKey]; } else { this[sourceKey] = ValueSource.Local; - if (valueConverter) { + if (valueConverter && typeof value === "string") { value = valueConverter(value); } } @@ -254,7 +374,12 @@ export class CssProperty { let changed: boolean = equalityComparer ? !equalityComparer(currentValue, value) : currentValue !== value; if (changed) { - this[key] = value; + if (reset) { + delete this[key]; + } else { + this[key] = value; + } + if (valueChanged) { valueChanged(this, currentValue, value); } @@ -284,8 +409,7 @@ export class CssProperty { } function setCssValue(this: T, value: U): void { - let resetValue = value === unsetValue; - let newValueSource: ValueSource; + let reset = value === unsetValue; let currentValueSource: number = this[sourceKey] || ValueSource.Default; // We have localValueSource - NOOP. @@ -293,22 +417,26 @@ export class CssProperty { return; } - if (resetValue) { + if (reset) { value = defaultValue; - newValueSource = ValueSource.Default; + delete this[sourceKey]; } else { - newValueSource = ValueSource.Css; - if (valueConverter) { + this[sourceKey] = ValueSource.Css; + if (valueConverter && typeof value === "string") { value = valueConverter(value); } } let currentValue: U = key in this ? this[key] : defaultValue; let changed: boolean = equalityComparer ? !equalityComparer(currentValue, value) : currentValue !== value; - this[sourceKey] = newValueSource; if (changed) { - this[key] = value; + if (reset) { + delete this[key]; + } else { + this[key] = value; + } + if (valueChanged) { valueChanged(this, currentValue, value); } @@ -395,9 +523,9 @@ export class InheritedCssProperty extends CssProperty let dependentPropertyNativeKey = dependentProperty ? dependentProperty.native : undefined; let setFunc = (valueSource: ValueSource) => function (this: T, value: any): void { - let resetValue = value === unsetValue; + let reset = value === unsetValue; let currentValueSource: number = this[sourceKey] || ValueSource.Default; - if (resetValue) { + if (reset) { // If we want to reset cssValue and we have localValue - return; if (valueSource === ValueSource.Css && currentValueSource === ValueSource.Local) { return; @@ -410,7 +538,7 @@ export class InheritedCssProperty extends CssProperty let view = this.view; let newValue: U; - if (resetValue) { + if (reset) { // If unsetValue - we want to reset this property. let parent = view.parent; let style = parent ? parent.style : null @@ -421,11 +549,11 @@ export class InheritedCssProperty extends CssProperty } else { newValue = defaultValue; - this[sourceKey] = ValueSource.Default; + delete this[sourceKey]; } } else { this[sourceKey] = valueSource; - if (valueConverter) { + if (valueConverter && typeof value === "string") { newValue = valueConverter(value); } else { newValue = value; @@ -436,7 +564,12 @@ export class InheritedCssProperty extends CssProperty let changed: boolean = equalityComparer ? !equalityComparer(currentValue, newValue) : currentValue !== newValue; if (changed) { - this[key] = newValue; + if (reset) { + delete this[key]; + } else { + this[key] = newValue; + } + if (valueChanged) { valueChanged(this, currentValue, newValue); } @@ -466,7 +599,7 @@ export class InheritedCssProperty extends CssProperty view.eachChild((child) => { let childStyle = child.style; let childValueSource = childStyle[sourceKey] || ValueSource.Default; - if (resetValue) { + if (reset) { if (childValueSource === ValueSource.Inherited) { setDefaultFunc.call(childStyle, unsetValue); } diff --git a/tns-core-modules/ui/definitions.d.ts b/tns-core-modules/ui/definitions.d.ts index 2caa4057f..29ae448cf 100644 --- a/tns-core-modules/ui/definitions.d.ts +++ b/tns-core-modules/ui/definitions.d.ts @@ -62,6 +62,10 @@ declare module "ui/core/properties" { readonly valueConverter?: (value: any) => U } + export interface CoerciblePropertyOptions extends PropertyOptions { + coerceValue(T, U): U + } + interface CssPropertyOptions extends PropertyOptions { readonly cssName: string; } @@ -73,6 +77,14 @@ declare module "ui/core/properties" { public register(cls: { prototype: T }): void; } + class CoercibleProperty implements TypedPropertyDescriptor { + constructor(options: CoerciblePropertyOptions); + + public readonly native: symbol; + public readonly coerce: (target: T) => void; + public register(cls: { prototype: T }): void; + } + class InheritedProperty extends Property { constructor(options: PropertyOptions); } diff --git a/tns-core-modules/ui/tab-view/tab-view-common.ts b/tns-core-modules/ui/tab-view/tab-view-common.ts index 9cf01fca9..041796a32 100644 --- a/tns-core-modules/ui/tab-view/tab-view-common.ts +++ b/tns-core-modules/ui/tab-view/tab-view-common.ts @@ -1,5 +1,5 @@ import { TabView as TabViewDefinition, TabViewItem as TabViewItemDefinition } from "ui/tab-view"; -import { Property } from "ui/core/properties"; +import { Property, CoercibleProperty } from "ui/core/properties"; import { View, AddArrayFromBuilder } from "ui/core/view"; import { Bindable } from "ui/core/bindable"; import { isIOS } from "platform"; @@ -122,20 +122,6 @@ export class TabViewBase extends View implements TabViewDefinition, AddArrayFrom } } - public _onSelectedIndexPropertyChangedSetNativeValue() { - let index = this.selectedIndex; - if (index < 0) { - return; - } - - if (this.items) { - if (index >= this.items.length) { - this.selectedIndex = unsetValue; - throw new Error("SelectedIndex should be between [0, items.length)"); - } - } - } - get _selectedView(): View { let items = this.items; let selectedIndex = this.selectedIndex; @@ -175,7 +161,7 @@ export class TabViewBase extends View implements TabViewDefinition, AddArrayFrom public _onBindingContextChanged(oldValue: any, newValue: any) { super._onBindingContextChanged(oldValue, newValue); if (this.items && this.items.length > 0) { - + for (let i = 0, length = this.items.length; i < length; i++) { this.items[i].bindingContext = newValue; } @@ -191,13 +177,6 @@ export class TabViewBase extends View implements TabViewDefinition, AddArrayFrom // iOS sepcific } - get [selectedIndexProperty.native](): number { - return -1; - } - set [selectedIndexProperty.native](value: number) { - this._onSelectedIndexPropertyChangedSetNativeValue(); - } - public onItemsPropertyChanged(oldValue: TabViewItemDefinition[], newValue: TabViewItemDefinition[]) { this.previousItems = oldValue; } @@ -206,11 +185,29 @@ export class TabViewBase extends View implements TabViewDefinition, AddArrayFrom export const itemsProperty = new Property({ name: "items", valueChanged: (target, oldValue, newValue) => { target.onItemsPropertyChanged(oldValue, newValue); + selectedIndexProperty.coerce(target); } }); itemsProperty.register(TabViewBase); -export const selectedIndexProperty = new Property({ name: "selectedIndex", defaultValue: -1, affectsLayout: isIOS }); +export const selectedIndexProperty = new CoercibleProperty({ + name: "selectedIndex", defaultValue: -1, affectsLayout: isIOS, valueChanged: (target, oldValue, newValue) => { + let args = { eventName: TabViewBase.selectedIndexChangedEvent, object: this, oldIndex: oldValue, newIndex: newValue }; + target.notify(args); + }, coerceValue: (target, value) => { + let items = target.items; + if (items) { + let max = items.length - 1; + if (value > max) { + value = max; + } + } else { + value = -1; + } + + return value; + } +}); selectedIndexProperty.register(TabViewBase); export const selectedColorProperty = new Property({ name: "selectedColor" }); diff --git a/tns-core-modules/ui/tab-view/tab-view.android.ts b/tns-core-modules/ui/tab-view/tab-view.android.ts index 205b5ee36..6fc93818c 100644 --- a/tns-core-modules/ui/tab-view/tab-view.android.ts +++ b/tns-core-modules/ui/tab-view/tab-view.android.ts @@ -1,17 +1,16 @@ import { TabViewBase, TabViewItemBase, itemsProperty, selectedIndexProperty, selectedColorProperty, tabsBackgroundColorProperty, traceCategory } from "./tab-view-common" -import { View, colorProperty, fontInternalProperty } from "ui/core/view"; +import { View, colorProperty, fontInternalProperty, layout } from "ui/core/view"; import { Property } from "ui/core/properties"; import { Bindable } from "ui/core/bindable"; import { isIOS } from "platform"; import { Color } from "color"; import { Font } from "ui/styling/font"; import { fromFileOrResource } from "image-source"; -import trace = require("trace"); +import { enabled as traceEnabled, write as traceWrite } from "trace"; +import { RESOURCE_PREFIX, ad } from "utils/utils"; export * from "./tab-view-common"; - - const VIEWS_STATES = "_viewStates"; const ACCENT_COLOR = "colorAccent"; const PRIMARY_COLOR = "colorPrimary"; @@ -59,8 +58,8 @@ function ensurePagerAdapterClass() { } instantiateItem(container: android.view.ViewGroup, index: number) { - if (trace.enabled) { - trace.write("TabView.PagerAdapter.instantiateItem; container: " + container + "; index: " + index, traceCategory); + if (traceEnabled) { + traceWrite("TabView.PagerAdapter.instantiateItem; container: " + container + "; index: " + index, traceCategory); } let item = this.items[index]; @@ -69,8 +68,8 @@ function ensurePagerAdapterClass() { } if (this[VIEWS_STATES]) { - if (trace.enabled) { - trace.write("TabView.PagerAdapter.instantiateItem; restoreHierarchyState: " + item.view, traceCategory); + if (traceEnabled) { + traceWrite("TabView.PagerAdapter.instantiateItem; restoreHierarchyState: " + item.view, traceCategory); } item.view._nativeView.restoreHierarchyState(this[VIEWS_STATES]); } @@ -80,8 +79,8 @@ function ensurePagerAdapterClass() { } destroyItem(container: android.view.ViewGroup, index: number, _object: any) { - if (trace.enabled) { - trace.write("TabView.PagerAdapter.destroyItem; container: " + container + "; index: " + index + "; _object: " + _object, traceCategory); + if (traceEnabled) { + traceWrite("TabView.PagerAdapter.destroyItem; container: " + container + "; index: " + index + "; _object: " + _object, traceCategory); } let item = this.items[index]; let nativeView = item.view._nativeView; @@ -104,8 +103,8 @@ function ensurePagerAdapterClass() { } saveState(): android.os.Parcelable { - if (trace.enabled) { - trace.write("TabView.PagerAdapter.saveState", traceCategory); + if (traceEnabled) { + traceWrite("TabView.PagerAdapter.saveState", traceCategory); } let owner: TabView = this.owner; @@ -132,8 +131,8 @@ function ensurePagerAdapterClass() { } restoreState(state: android.os.Parcelable, loader: java.lang.ClassLoader) { - if (trace.enabled) { - trace.write("TabView.PagerAdapter.restoreState", traceCategory); + if (traceEnabled) { + traceWrite("TabView.PagerAdapter.restoreState", traceCategory); } let bundle: android.os.Bundle = state; bundle.setClassLoader(loader); @@ -180,8 +179,8 @@ export class TabView extends TabViewBase { } public _createUI() { - if (trace.enabled) { - trace.write("TabView._createUI(" + this + ");", traceCategory); + if (traceEnabled) { + traceWrite("TabView._createUI(" + this + ");", traceCategory); } this._grid = new org.nativescript.widgets.GridLayout(this._context); @@ -193,12 +192,12 @@ export class TabView extends TabViewBase { this.setElevation(); - let accentColor = utils.ad.resources.getPalleteColor(ACCENT_COLOR, this._context); + let accentColor = ad.resources.getPalleteColor(ACCENT_COLOR, this._context); if (accentColor) { this._tabLayout.setSelectedIndicatorColors([accentColor]); } - let primaryColor = utils.ad.resources.getPalleteColor(PRIMARY_COLOR, this._context); + let primaryColor = ad.resources.getPalleteColor(PRIMARY_COLOR, this._context); if (primaryColor) { this._tabLayout.setBackgroundColor(primaryColor); } @@ -225,7 +224,7 @@ export class TabView extends TabViewBase { private setElevation() { let compat = android.support.v4.view.ViewCompat; if (compat.setElevation) { - let val = DEFAULT_ELEVATION * utils.layout.getDisplayDensity(); + let val = DEFAULT_ELEVATION * layout.getDisplayDensity(); compat.setElevation(this._grid, val); compat.setElevation(this._tabLayout, val); } @@ -271,7 +270,8 @@ export class TabView extends TabViewBase { } public _updateTabForItem(item: TabViewItem) { - if (this.items && this.items.length > 0) { + let items = this.items; + if (items && items.length > 0) { let index = this.items.indexOf(item); if (index >= 0) { this._tabLayout.updateItemAt(index, this.createTabItem(item)); @@ -279,39 +279,17 @@ export class TabView extends TabViewBase { } } - public _onSelectedIndexPropertyChangedSetNativeValue() { - if (trace.enabled) { - trace.write("TabView._onSelectedIndexPropertyChangedSetNativeValue(" + data.oldValue + " ---> " + data.newValue + ");", traceCategory); - } - super._onSelectedIndexPropertyChangedSetNativeValue(data); - - let index = data.newValue; - if (!types.isNullOrUndefined(index)) { - // Select the respective page in the ViewPager - let viewPagerSelectedIndex = this._viewPager.getCurrentItem(); - if (viewPagerSelectedIndex !== index) { - if (trace.enabled) { - trace.write("TabView this._viewPager.setCurrentItem(" + index + ", true);", traceCategory); - } - this._viewPager.setCurrentItem(index, true); - } - } - - let args = { eventName: TabView.selectedIndexChangedEvent, object: this, oldIndex: data.oldValue, newIndex: data.newValue }; - this.notify(args); - } - private createTabItem(item: TabViewItem): org.nativescript.widgets.TabItemSpec { let result = new org.nativescript.widgets.TabItemSpec(); result.title = item.title; if (item.iconSource) { - if (item.iconSource.indexOf(utils.RESOURCE_PREFIX) === 0) { - result.iconId = utils.ad.resources.getDrawableId(item.iconSource.substr(utils.RESOURCE_PREFIX.length)); + if (item.iconSource.indexOf(RESOURCE_PREFIX) === 0) { + result.iconId = ad.resources.getDrawableId(item.iconSource.substr(RESOURCE_PREFIX.length)); } else { - let is = imageSource.fromFileOrResource(item.iconSource); + let is = fromFileOrResource(item.iconSource); if (is) { result.iconDrawable = new android.graphics.drawable.BitmapDrawable(is.android); } @@ -321,6 +299,16 @@ export class TabView extends TabViewBase { return result; } + get [selectedIndexProperty.native](): number { + return -1; + } + set [selectedIndexProperty.native](value: number) { + if (traceEnabled) { + traceWrite("TabView this._viewPager.setCurrentItem(" + value + ", true);", traceCategory); + } + this._viewPager.setCurrentItem(value, true); + } + get [itemsProperty.native](): TabViewItemBase[] { return null; } diff --git a/tns-core-modules/ui/tab-view/tab-view.ios.ts b/tns-core-modules/ui/tab-view/tab-view.ios.ts index 936cf0e96..6da5a80ba 100644 --- a/tns-core-modules/ui/tab-view/tab-view.ios.ts +++ b/tns-core-modules/ui/tab-view/tab-view.ios.ts @@ -1,24 +1,15 @@ -import common = require("./tab-view-common"); -import definition = require("ui/tab-view"); -import dependencyObservable = require("ui/core/dependency-observable"); -import trace = require("trace"); -import view = require("ui/core/view"); -import types = require("utils/types"); -import color = require("color"); -import * as imageSourceModule from "image-source"; -import style = require("ui/styling/style"); +import { TabViewBase, TabViewItemBase, itemsProperty, selectedIndexProperty, selectedColorProperty, tabsBackgroundColorProperty, traceCategory } from "./tab-view-common" +import { View, colorProperty, fontInternalProperty, layout } from "ui/core/view"; +import { Color } from "color"; +import { Font } from "ui/styling/font"; +import { fromFileOrResource } from "image-source"; import { Page } from "ui/page"; -import * as utils from "utils/utils"; -import getter = utils.ios.getter; +import { ios } from "utils/utils"; +import { enabled as traceEnabled, write as traceWrite, categories as traceCategories } from "trace"; -global.moduleMerge(common, exports); +import getter = ios.getter; -var imageSource: typeof imageSourceModule; -function ensureImageSource() { - if (!imageSource) { - imageSource = require("image-source"); - } -} +export * from "./tab-view-common"; class UITabBarControllerImpl extends UITabBarController { @@ -31,8 +22,8 @@ class UITabBarControllerImpl extends UITabBarController { } public viewDidLayoutSubviews(): void { - if (trace.enabled) { - trace.write("TabView.UITabBarControllerClass.viewDidLayoutSubviews();", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView.UITabBarControllerClass.viewDidLayoutSubviews();", traceCategories.Debug); } super.viewDidLayoutSubviews(); let owner = this._owner.get(); @@ -54,25 +45,25 @@ class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControl } public tabBarControllerShouldSelectViewController(tabBarController: UITabBarController, viewController: UIViewController): boolean { - if (trace.enabled) { - trace.write("TabView.delegate.SHOULD_select(" + tabBarController + ", " + viewController + ");", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView.delegate.SHOULD_select(" + tabBarController + ", " + viewController + ");", traceCategories.Debug); } - + let owner = this._owner.get(); if (owner) { // "< More" cannot be visible after clicking on the main tab bar buttons. let backToMoreWillBeVisible = false; owner._handleTwoNavigationBars(backToMoreWillBeVisible); } - - return true; + + return true; } - + public tabBarControllerDidSelectViewController(tabBarController: UITabBarController, viewController: UIViewController): void { - if (trace.enabled) { - trace.write("TabView.delegate.DID_select(" + tabBarController + ", " + viewController + ");", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView.delegate.DID_select(" + tabBarController + ", " + viewController + ");", traceCategories.Debug); } - + let owner = this._owner.get(); if (owner) { owner._onViewControllerShown(viewController); @@ -92,10 +83,10 @@ class UINavigationControllerDelegateImpl extends NSObject implements UINavigatio } navigationControllerWillShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void { - if (trace.enabled) { - trace.write("TabView.moreNavigationController.WILL_show(" + navigationController + ", " + viewController + ", " + animated + ");", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView.moreNavigationController.WILL_show(" + navigationController + ", " + viewController + ", " + animated + ");", traceCategories.Debug); } - + let owner = this._owner.get(); if (owner) { // If viewController is one of our tab item controllers, then "< More" will be visible shortly. @@ -106,8 +97,8 @@ class UINavigationControllerDelegateImpl extends NSObject implements UINavigatio } navigationControllerDidShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void { - if (trace.enabled) { - trace.write("TabView.moreNavigationController.DID_show(" + navigationController + ", " + viewController + ", " + animated + ");", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView.moreNavigationController.DID_show(" + navigationController + ", " + viewController + ", " + animated + ");", traceCategories.Debug); } // We don't need Edit button in More screen. navigationController.navigationBar.topItem.rightBarButtonItem = null; @@ -118,25 +109,29 @@ class UINavigationControllerDelegateImpl extends NSObject implements UINavigatio } } -export class TabViewItem extends common.TabViewItem { +function updateItemTitlePosition(tabBarItem: UITabBarItem): void { + if (typeof (tabBarItem).setTitlePositionAdjustment === "function") { + (tabBarItem).setTitlePositionAdjustment({ horizontal: 0, vertical: -20 }); + } + else { + tabBarItem.titlePositionAdjustment = { horizontal: 0, vertical: -20 }; + } +} + +export class TabViewItem extends TabViewItemBase { public _controller: UIViewController; public _parent: TabView; public _update() { if (this._parent && this._controller) { - var icon = this._parent._getIcon(this.iconSource); - //TODO: Implement initWithSystemItem to support standard system icons - var tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((this.title || ""), icon, this._parent.items.indexOf(this)); + const icon = this._parent._getIcon(this.iconSource); + //TODO: Implement initWithSystemItem to support standard system icons + const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((this.title || ""), icon, this._parent.items.indexOf(this)); if (!icon) { - if (types.isFunction((tabBarItem).setTitlePositionAdjustment)) { - (tabBarItem).setTitlePositionAdjustment({ horizontal: 0, vertical: -20 }); - } - else { - tabBarItem.titlePositionAdjustment = { horizontal: 0, vertical: -20 }; - } + updateItemTitlePosition(tabBarItem); } - var states = getTitleAttributesForStates(this._parent); + const states = getTitleAttributesForStates(this._parent); tabBarItem.setTitleTextAttributesForState(states.normalState, UIControlState.Normal); tabBarItem.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected); this._controller.tabBarItem = tabBarItem; @@ -144,7 +139,21 @@ export class TabViewItem extends common.TabViewItem { } } -export class TabView extends common.TabView { +function selectedColorPropertyChanged(data: dependencyObservable.PropertyChangeData) { + const tabView = data.object; + tabView._updateIOSTabBarColorsAndFonts(); +} +(common.TabView.selectedColorProperty.metadata).onSetNativeValue = selectedColorPropertyChanged; + +function tabsBackgroundColorPropertyChanged(data: dependencyObservable.PropertyChangeData) { + const tabView = data.object; + if (data.newValue instanceof color.Color) { + tabView.ios.tabBar.barTintColor = data.newValue.ios; + } +} +(common.TabView.tabsBackgroundColorProperty.metadata).onSetNativeValue = tabsBackgroundColorPropertyChanged; + +export class TabView extends TabViewBase { public _ios: UITabBarControllerImpl; private _delegate: UITabBarControllerDelegateImpl; private _moreNavigationControllerDelegate: UINavigationControllerDelegateImpl; @@ -182,63 +191,60 @@ export class TabView extends common.TabView { public _onViewControllerShown(viewController: UIViewController) { // This method could be called with the moreNavigationController or its list controller, so we have to check. - if (trace.enabled) { - trace.write("TabView._onViewControllerShown(" + viewController + ");", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView._onViewControllerShown(" + viewController + ");", traceCategories.Debug); } if (this._ios.viewControllers.containsObject(viewController)) { this.selectedIndex = this._ios.viewControllers.indexOfObject(viewController); } else { - if (trace.enabled) { - trace.write("TabView._onViewControllerShown: viewController is not one of our viewControllers", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView._onViewControllerShown: viewController is not one of our viewControllers", traceCategories.Debug); } } } - + private _actionBarHiddenByTabView: boolean; - public _handleTwoNavigationBars(backToMoreWillBeVisible: boolean){ - if (trace.enabled) { - trace.write(`TabView._handleTwoNavigationBars(backToMoreWillBeVisible: ${backToMoreWillBeVisible})`, trace.categories.Debug); + public _handleTwoNavigationBars(backToMoreWillBeVisible: boolean) { + if (traceEnabled) { + traceWrite(`TabView._handleTwoNavigationBars(backToMoreWillBeVisible: ${backToMoreWillBeVisible})`, traceCategories.Debug); } - + // The "< Back" and "< More" navigation bars should not be visible simultaneously. let page = this.page; let actionBarVisible = page.frame._getNavBarVisible(page); - - if (backToMoreWillBeVisible && actionBarVisible){ + + if (backToMoreWillBeVisible && actionBarVisible) { page.frame.ios._disableNavBarAnimation = true; page.actionBarHidden = true; page.frame.ios._disableNavBarAnimation = false; this._actionBarHiddenByTabView = true; - if (trace.enabled) { - trace.write(`TabView hid action bar`, trace.categories.Debug); + if (traceEnabled) { + traceWrite(`TabView hid action bar`, traceCategories.Debug); } return; } - - if (!backToMoreWillBeVisible && this._actionBarHiddenByTabView){ + + if (!backToMoreWillBeVisible && this._actionBarHiddenByTabView) { page.frame.ios._disableNavBarAnimation = true; page.actionBarHidden = false; page.frame.ios._disableNavBarAnimation = false; this._actionBarHiddenByTabView = undefined; - if (trace.enabled) { - trace.write(`TabView restored action bar`, trace.categories.Debug); + if (traceEnabled) { + traceWrite(`TabView restored action bar`, traceCategories.Debug); } return; } } - public _removeTabs(oldItems: Array) { - if (trace.enabled) { - trace.write("TabView._removeTabs(" + oldItems + ");", trace.categories.Debug); + public _removeTabs(oldItems: Array) { + if (traceEnabled) { + traceWrite("TabView._removeTabs(" + oldItems + ");", traceCategories.Debug); } super._removeTabs(oldItems); - var i: number; - var length = oldItems.length; - var oldItem: TabViewItem; - for (i = 0; i < length; i++) { - oldItem = oldItems[i]; + for (let i = 0, length = oldItems.length; i < length; i++) { + let oldItem = oldItems[i]; oldItem._parent = null; oldItem._controller = null; } @@ -246,22 +252,19 @@ export class TabView extends common.TabView { this._ios.viewControllers = null; } - public _addTabs(newItems: Array) { - if (trace.enabled) { - trace.write("TabView._addTabs(" + newItems + ");", trace.categories.Debug); + public _addTabs(newItems: Array) { + if (traceEnabled) { + traceWrite("TabView._addTabs(" + newItems + ");", traceCategories.Debug); } super._addTabs(newItems); - var i: number; - var length = newItems.length; - var item: TabViewItem; - var newControllers: NSMutableArray = NSMutableArray.alloc().initWithCapacity(length); - var newController: UIViewController; + const length = newItems.length; + const newControllers: NSMutableArray = NSMutableArray.alloc().initWithCapacity(length); + const states = getTitleAttributesForStates(this); - var states = getTitleAttributesForStates(this); - - for (i = 0; i < length; i++) { - item = newItems[i]; + for (let i = 0; i < length; i++) { + const item = newItems[i]; + let newController: UIViewController; if (item.view.ios instanceof UIViewController) { newController = item.view.ios; @@ -273,16 +276,11 @@ export class TabView extends common.TabView { item._parent = this; item._controller = newController; - var icon = this._getIcon(item.iconSource); + const icon = this._getIcon(item.iconSource); - var tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((item.title || ""), icon, i); + const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((item.title || ""), icon, i); if (!icon) { - if (types.isFunction((tabBarItem).setTitlePositionAdjustment)) { - (tabBarItem).setTitlePositionAdjustment({ horizontal: 0, vertical: -20 }); - } - else { - (tabBarItem).titlePositionAdjustment = { horizontal: 0, vertical: -20 }; - } + updateItemTitlePosition(tabBarItem); } tabBarItem.setTitleTextAttributesForState(states.normalState, UIControlState.Normal); tabBarItem.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected); @@ -337,14 +335,11 @@ export class TabView extends common.TabView { return null; } - var image: UIImage; - image = this._iconsCache[iconSource]; + let image: UIImage = this._iconsCache[iconSource]; if (!image) { - ensureImageSource(); - - var is = imageSource.fromFileOrResource(iconSource); + const is = fromFileOrResource(iconSource); if (is && is.ios) { - var originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode()); + const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode()); this._iconsCache[iconSource] = originalRenderedImage; image = originalRenderedImage; } @@ -357,15 +352,15 @@ export class TabView extends common.TabView { super._onSelectedIndexPropertyChangedSetNativeValue(data); var newIndex = data.newValue; - if (trace.enabled) { - trace.write("TabView._onSelectedIndexPropertyChangedSetNativeValue(" + newIndex + ")", trace.categories.Debug); + if (traceEnabled) { + traceWrite("TabView._onSelectedIndexPropertyChangedSetNativeValue(" + newIndex + ")", traceCategories.Debug); } if (types.isNullOrUndefined(newIndex)) { return; } this._ios.selectedIndex = data.newValue; - + // We will need to measure and arrange what became this._selectedView this.requestLayout(); @@ -374,37 +369,38 @@ export class TabView extends common.TabView { } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { - var nativeView = this._nativeView; + const nativeView = this._nativeView; if (nativeView) { - var width = utils.layout.getMeasureSpecSize(widthMeasureSpec); - var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec); + const width = layout.getMeasureSpecSize(widthMeasureSpec); + const widthMode = layout.getMeasureSpecMode(widthMeasureSpec); - var height = utils.layout.getMeasureSpecSize(heightMeasureSpec); - var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); + const height = layout.getMeasureSpecSize(heightMeasureSpec); + const heightMode = layout.getMeasureSpecMode(heightMeasureSpec); this._tabBarHeight = TabView.measureHelper(this._ios.tabBar, width, widthMode, height, heightMode).height; let moreNavBarVisible = !!this._ios.moreNavigationController.navigationBar.window; this._navBarHeight = moreNavBarVisible ? TabView.measureHelper(this._ios.moreNavigationController.navigationBar, width, widthMode, height, heightMode).height : 0; - var density = utils.layout.getDisplayDensity(); - var measureWidth = 0; - var measureHeight = 0; + const density = layout.getDisplayDensity(); + let measureWidth = 0; + let measureHeight = 0; - var child = this._selectedView; + const child = this._selectedView; if (child) { - var childHeightMeasureSpec = utils.layout.makeMeasureSpec(height - this._navBarHeight - this._tabBarHeight, heightMode); - var childSize = view.View.measureChild(this, child, widthMeasureSpec, childHeightMeasureSpec); + const childHeightMeasureSpec = layout.makeMeasureSpec(height - this._navBarHeight - this._tabBarHeight, heightMode); + const childSize = View.measureChild(this, child, widthMeasureSpec, childHeightMeasureSpec); measureHeight = childSize.measuredHeight; measureWidth = childSize.measuredWidth; } - measureWidth = Math.max(measureWidth, this.minWidth * density); - measureHeight = Math.max(measureHeight, this.minHeight * density); + let style = this.style; + measureWidth = Math.max(measureWidth, style.effectiveMinWidth * density); + measureHeight = Math.max(measureHeight, style.effectiveMinHeight * density); - var widthAndState = view.View.resolveSizeAndState(measureWidth, width, widthMode, 0); - var heightAndState = view.View.resolveSizeAndState(measureHeight, height, heightMode, 0); + const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0); + const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0); this.setMeasuredDimension(widthAndState, heightAndState); } @@ -413,16 +409,16 @@ export class TabView extends common.TabView { public onLayout(left: number, top: number, right: number, bottom: number): void { super.onLayout(left, top, right, bottom); - var child = this._selectedView; + const child = this._selectedView; if (child) { - view.View.layoutChild(this, child, 0, this._navBarHeight, right, (bottom - this._navBarHeight - this._tabBarHeight)); + View.layoutChild(this, child, 0, this._navBarHeight, right, (bottom - this._navBarHeight - this._tabBarHeight)); } } private static measureHelper(nativeView: UIView, width: number, widthMode: number, height: number, heightMode: number): CGSize { return nativeView.sizeThatFits(CGSizeMake( - (widthMode === utils.layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : width, - (heightMode === utils.layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : height)); + (widthMode === layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : width, + (heightMode === layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : height)); } public _updateIOSTabBarColorsAndFonts(): void { @@ -430,10 +426,13 @@ export class TabView extends common.TabView { return; } - var tabBar = this.ios.tabBar; - var states = getTitleAttributesForStates(this); - for (var i = 0; i < tabBar.items.count; i++) { - var item = tabBar.items[i]; + const tabBar = this.ios.tabBar; + + tabBar.tintColor = this.selectedColor ? this.selectedColor.ios : null; + const states = getTitleAttributesForStates(this); + + for (let i = 0; i < tabBar.items.count; i++) { + const item = tabBar.items[i]; item.setTitleTextAttributesForState(states.normalState, UIControlState.Normal); item.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected); } @@ -459,21 +458,21 @@ export class TabView extends common.TabView { } function getTitleAttributesForStates(tabView: TabView): { normalState: any, selectedState: any } { - var normalState = {}; - if (tabView.tabTextColor instanceof color.Color) { + const normalState = {}; + if (tabView.tabTextColor instanceof Color) { normalState[UITextAttributeTextColor] = tabView.tabTextColor.ios; } - var selectedState = {}; - if (tabView.selectedTabTextColor instanceof color.Color) { + const selectedState = {}; + if (tabView.selectedTabTextColor instanceof Color) { selectedState[UITextAttributeTextColor] = tabView.selectedTabTextColor.ios; } else { selectedState[UITextAttributeTextColor] = tabView.ios.tabBar.tintColor; } - var defaultFont = UIFont.systemFontOfSize(10); - var font = (tabView.style)._fontInternal.getUIFont(defaultFont); + const defaultFont = UIFont.systemFontOfSize(10); + const font = (tabView.style)._fontInternal.getUIFont(defaultFont); normalState[NSFontAttributeName] = font; selectedState[NSFontAttributeName] = font; @@ -596,4 +595,4 @@ export class TabViewStyler implements style.Styler { } } -TabViewStyler.registerHandlers(); \ No newline at end of file +TabViewStyler.registerHandlers(); diff --git a/tns-core-modules/utils/utils.ios.ts b/tns-core-modules/utils/utils.ios.ts index 9cdd259d9..b540f0d8c 100644 --- a/tns-core-modules/utils/utils.ios.ts +++ b/tns-core-modules/utils/utils.ios.ts @@ -1,6 +1,6 @@ import dts = require("utils/utils"); import common = require("./utils-common"); -import {Color} from "color"; +import { Color } from "color"; import enums = require("ui/enums"); import * as fsModule from "file-system"; import * as traceModule from "trace"; @@ -56,9 +56,9 @@ export module ios { } } - export function getter(_this: any, property: T | {(): T}): T { + export function getter(_this: any, property: T | { (): T }): T { if (typeof property === "function") { - return (<{(): T}>property).call(_this); + return (<{ (): T }>property).call(_this); } else { return property; }