Implement Page background option to span under status bar (iOS only for the moment)

This commit is contained in:
hshristov
2015-10-06 14:35:59 +03:00
parent a853726a30
commit 0ecb5d4dcd
11 changed files with 196 additions and 105 deletions

View File

@ -154,11 +154,16 @@ export class ActionBar extends common.ActionBar {
let frame = <frameModule.Frame>this.page.frame; let frame = <frameModule.Frame>this.page.frame;
if (frame) { if (frame) {
let navBar: UIView = frame.ios.controller.navigationBar; let navBar: UIView = frame.ios.controller.navigationBar;
let navBarSize = navBar.sizeThatFits(CGSizeMake(width, height)); if (!navBar.hidden) {
let navBarSize = navBar.sizeThatFits(CGSizeMake(
(widthMode === utils.layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : width,
(heightMode === utils.layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : height));
navBarWidth = navBarSize.width; navBarWidth = navBarSize.width;
this._navigationBarHeight = navBarHeight = navBarSize.height; navBarHeight = navBarSize.height;
}
} }
this._navigationBarHeight = navBarHeight;
if (this.titleView) { if (this.titleView) {
view.View.measureChild(this, this.titleView, view.View.measureChild(this, this.titleView,
utils.layout.makeMeasureSpec(width, utils.layout.AT_MOST), utils.layout.makeMeasureSpec(width, utils.layout.AT_MOST),

View File

@ -244,7 +244,7 @@ export class View extends viewCommon.View {
// When in landscape in iOS 7 there is transformation on the first subview of the window so we set frame to its subview. // When in landscape in iOS 7 there is transformation on the first subview of the window so we set frame to its subview.
// in iOS 8 we set frame to subview again otherwise we get clipped. // in iOS 8 we set frame to subview again otherwise we get clipped.
var nativeView: UIView; var nativeView: UIView;
if (!this.parent && this._nativeView.subviews.count > 0 && !(<any>this)._isModal) { if (!this.parent && this._nativeView.subviews.count > 0 && utils.ios.MajorVersion < 8) {
trace.write(this + " has no parent. Setting frame to first child instead.", trace.categories.Layout); trace.write(this + " has no parent. Setting frame to first child instead.", trace.categories.Layout);
nativeView = (<UIView>this._nativeView.subviews[0]); nativeView = (<UIView>this._nativeView.subviews[0]);
} }
@ -287,9 +287,7 @@ export class CustomLayoutView extends View {
constructor() { constructor() {
super(); super();
this._view = new UIView(); this._view = new UIView();
this._view.autoresizesSubviews = false;
} }
get ios(): UIView { get ios(): UIView {

View File

@ -6,6 +6,7 @@ import enums = require("ui/enums");
import utils = require("utils/utils"); import utils = require("utils/utils");
import view = require("ui/core/view"); import view = require("ui/core/view");
import types = require("utils/types"); import types = require("utils/types");
import uiUtils = require("ui/utils");
global.moduleMerge(frameCommon, exports); global.moduleMerge(frameCommon, exports);
@ -182,27 +183,47 @@ export class Frame extends frameCommon.Frame {
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
var width = utils.layout.getMeasureSpecSize(widthMeasureSpec); let width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec); let widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
var height = utils.layout.getMeasureSpecSize(heightMeasureSpec); let height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); let heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
this._widthMeasureSpec = widthMeasureSpec; this._widthMeasureSpec = widthMeasureSpec;
this._heightMeasureSpec = heightMeasureSpec; this._heightMeasureSpec = heightMeasureSpec;
var result = view.View.measureChild(this, this.currentPage, widthMeasureSpec, heightMeasureSpec); let result = this.measurePage(this.currentPage);
let widthAndState = view.View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0);
var widthAndState = view.View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0); let heightAndState = view.View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);
var heightAndState = view.View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState); this.setMeasuredDimension(widthAndState, heightAndState);
} }
public measurePage(page: pages.Page): { measuredWidth: number; measuredHeight: number } {
// If background does not span under statusbar - reduce available height.
let heightSpec: number = this._heightMeasureSpec;
if (page && !page.backgroundSpanUnderStatusBar) {
let height = utils.layout.getMeasureSpecSize(this._heightMeasureSpec);
let heightMode = utils.layout.getMeasureSpecMode(this._heightMeasureSpec);
let statusBarHeight = uiUtils.ios.getStatusBarHeight();
heightSpec = utils.layout.makeMeasureSpec(height - statusBarHeight, heightMode);
}
return view.View.measureChild(this, page, this._widthMeasureSpec, heightSpec);
}
public onLayout(left: number, top: number, right: number, bottom: number): void { public onLayout(left: number, top: number, right: number, bottom: number): void {
this._layoutWidth = right - left; this._layoutWidth = right - left;
this._layoutheight = bottom - top; this._layoutheight = bottom - top;
view.View.layoutChild(this, this.currentPage, 0, 0, right - left, bottom - top); this.layoutPage(this.currentPage);
}
public layoutPage(page: pages.Page): void {
// If background does not span under statusbar - reduce available height and adjust top offset.
let statusBarHeight = (page && !page.backgroundSpanUnderStatusBar) ? uiUtils.ios.getStatusBarHeight() : 0;
view.View.layoutChild(this, page, 0, statusBarHeight, this._layoutWidth, this._layoutheight);
} }
public get navigationBarHeight(): number { public get navigationBarHeight(): number {
@ -227,8 +248,6 @@ class UINavigationControllerImpl extends UINavigationController implements UINav
} }
public viewDidLoad(): void { public viewDidLoad(): void {
this.view.autoresizesSubviews = false;
this.view.autoresizingMask = UIViewAutoresizing.UIViewAutoresizingNone;
this._owner.onLoaded(); this._owner.onLoaded();
} }
@ -252,8 +271,8 @@ class UINavigationControllerImpl extends UINavigationController implements UINav
} }
frame._addView(newPage); frame._addView(newPage);
view.View.measureChild(frame, newPage, frame._widthMeasureSpec, frame._heightMeasureSpec); frame.measurePage(newPage);
view.View.layoutChild(frame, newPage, 0, 0, frame._layoutWidth, frame._layoutheight); frame.layoutPage(newPage)
} }
else if (newPage.parent !== frame) { else if (newPage.parent !== frame) {
throw new Error("Page is already shown on another frame."); throw new Error("Page is already shown on another frame.");

View File

@ -7,9 +7,7 @@ export class Layout extends layoutBase.LayoutBase implements definition.Layout {
constructor() { constructor() {
super(); super();
this._view = new UIView(); this._view = new UIView();
this._view.autoresizesSubviews = false;
} }
get ios(): UIView { get ios(): UIView {

View File

@ -142,7 +142,6 @@ export class ListView extends common.ListView {
this._ios = new UITableView(); this._ios = new UITableView();
this._ios.registerClassForCellReuseIdentifier(ListViewCell.class(), CELLIDENTIFIER); this._ios.registerClassForCellReuseIdentifier(ListViewCell.class(), CELLIDENTIFIER);
this._ios.autoresizesSubviews = false;
this._ios.autoresizingMask = UIViewAutoresizing.UIViewAutoresizingNone; this._ios.autoresizingMask = UIViewAutoresizing.UIViewAutoresizingNone;
this._ios.estimatedRowHeight = DEFAULT_HEIGHT; this._ios.estimatedRowHeight = DEFAULT_HEIGHT;

View File

@ -1,4 +1,4 @@
import contentView = require("ui/content-view"); import {ContentView} from "ui/content-view";
import view = require("ui/core/view"); import view = require("ui/core/view");
import dts = require("ui/page"); import dts = require("ui/page");
import frame = require("ui/frame"); import frame = require("ui/frame");
@ -6,17 +6,19 @@ import styleModule = require("../styling/style");
import styleScope = require("../styling/style-scope"); import styleScope = require("../styling/style-scope");
import fs = require("file-system"); import fs = require("file-system");
import frameCommon = require("../frame/frame-common"); import frameCommon = require("../frame/frame-common");
import actionBar = require("ui/action-bar"); import {ActionBar} from "ui/action-bar";
import dependencyObservable = require("ui/core/dependency-observable"); import {DependencyObservable, PropertyMetadata, PropertyMetadataSettings, PropertyChangeData, Property, ValueSource} from "ui/core/dependency-observable";
import proxy = require("ui/core/proxy"); import proxy = require("ui/core/proxy");
var actionBarHiddenProperty = new dependencyObservable.Property( // on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
"actionBarHidden", var AffectsLayout = global.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout;
"Page",
new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.AffectsLayout)
);
function onActionBarHiddenPropertyChanged(data: dependencyObservable.PropertyChangeData) { var backgroundSpanUnderStatusBarProperty = new Property("backgroundSpanUnderStatusBar", "Page", new proxy.PropertyMetadata(false, AffectsLayout));
var actionBarHiddenProperty = new Property("actionBarHidden", "Page", new proxy.PropertyMetadata(undefined, AffectsLayout));
function onActionBarHiddenPropertyChanged(data: PropertyChangeData) {
var page = <Page>data.object; var page = <Page>data.object;
if (page.isLoaded) { if (page.isLoaded) {
page._updateActionBar(data.newValue); page._updateActionBar(data.newValue);
@ -25,7 +27,8 @@ function onActionBarHiddenPropertyChanged(data: dependencyObservable.PropertyCha
(<proxy.PropertyMetadata>actionBarHiddenProperty.metadata).onSetNativeValue = onActionBarHiddenPropertyChanged; (<proxy.PropertyMetadata>actionBarHiddenProperty.metadata).onSetNativeValue = onActionBarHiddenPropertyChanged;
export class Page extends contentView.ContentView implements dts.Page { export class Page extends ContentView implements dts.Page {
public static backgroundSpanUnderStatusBarProperty = backgroundSpanUnderStatusBarProperty;
public static actionBarHiddenProperty = actionBarHiddenProperty; public static actionBarHiddenProperty = actionBarHiddenProperty;
public static navigatingToEvent = "navigatingTo"; public static navigatingToEvent = "navigatingTo";
public static navigatedToEvent = "navigatedTo"; public static navigatedToEvent = "navigatedTo";
@ -39,16 +42,16 @@ export class Page extends contentView.ContentView implements dts.Page {
private _cssApplied: boolean; private _cssApplied: boolean;
private _styleScope: styleScope.StyleScope = new styleScope.StyleScope(); private _styleScope: styleScope.StyleScope = new styleScope.StyleScope();
private _actionBar: actionBar.ActionBar; private _actionBar: ActionBar;
constructor(options?: dts.Options) { constructor(options?: dts.Options) {
super(options); super(options);
this.actionBar = new actionBar.ActionBar(); this.actionBar = new ActionBar();
} }
public onLoaded() { public onLoaded() {
// The default style of the page should be white background // The default style of the page should be white background
this.style._setValue(styleModule.backgroundColorProperty, "white", dependencyObservable.ValueSource.Inherited); this.style._setValue(styleModule.backgroundColorProperty, "white", ValueSource.Inherited);
this._applyCss(); this._applyCss();
@ -59,6 +62,14 @@ export class Page extends contentView.ContentView implements dts.Page {
super.onLoaded(); super.onLoaded();
} }
get backgroundSpanUnderStatusBar(): boolean {
return this._getValue(Page.backgroundSpanUnderStatusBarProperty);
}
set backgroundSpanUnderStatusBar(value: boolean) {
this._setValue(Page.backgroundSpanUnderStatusBarProperty, value);
}
get actionBarHidden(): boolean { get actionBarHidden(): boolean {
return this._getValue(Page.actionBarHiddenProperty); return this._getValue(Page.actionBarHiddenProperty);
} }
@ -86,10 +97,10 @@ export class Page extends contentView.ContentView implements dts.Page {
this._refreshCss(); this._refreshCss();
} }
get actionBar(): actionBar.ActionBar { get actionBar(): ActionBar {
return this._actionBar; return this._actionBar;
} }
set actionBar(value: actionBar.ActionBar) { set actionBar(value: ActionBar) {
if (!value) { if (!value) {
throw new Error("ActionBar cannot be null or undefined."); throw new Error("ActionBar cannot be null or undefined.");
} }
@ -198,7 +209,7 @@ export class Page extends contentView.ContentView implements dts.Page {
} }
public _addChildFromBuilder(name: string, value: any) { public _addChildFromBuilder(name: string, value: any) {
if (value instanceof actionBar.ActionBar) { if (value instanceof ActionBar) {
this.actionBar = value; this.actionBar = value;
} }
else { else {

10
ui/page/page.d.ts vendored
View File

@ -46,6 +46,11 @@ declare module "ui/page" {
* Represents a logical unit for navigation (inside Frame). * Represents a logical unit for navigation (inside Frame).
*/ */
export class Page extends contentView.ContentView { export class Page extends contentView.ContentView {
/**
* Dependency property that specify if page background should span under status bar.
*/
public static backgroundSpanUnderStatusBarProperty: dependencyObservable.Property;
/** /**
* Dependency property used to hide the Navigation Bar in iOS and the Action Bar in Android. * Dependency property used to hide the Navigation Bar in iOS and the Action Bar in Android.
*/ */
@ -78,6 +83,11 @@ declare module "ui/page" {
constructor(options?: Options) constructor(options?: Options)
/**
* Gets or sets whether page background spans under status bar.
*/
backgroundSpanUnderStatusBar: boolean;
/** /**
* Used to hide the Navigation Bar in iOS and the Action Bar in Android. * Used to hide the Navigation Bar in iOS and the Action Bar in Android.
*/ */

View File

@ -4,6 +4,8 @@ import {View} from "ui/core/view";
import trace = require("trace"); import trace = require("trace");
import uiUtils = require("ui/utils"); import uiUtils = require("ui/utils");
import utils = require("utils/utils"); import utils = require("utils/utils");
import {device} from "platform";
import {DeviceType} from "ui/enums";
global.moduleMerge(pageCommon, exports); global.moduleMerge(pageCommon, exports);
@ -22,23 +24,67 @@ class UIViewControllerImpl extends UIViewController {
public didRotateFromInterfaceOrientation(fromInterfaceOrientation: number) { public didRotateFromInterfaceOrientation(fromInterfaceOrientation: number) {
trace.write(this._owner + " didRotateFromInterfaceOrientation(" + fromInterfaceOrientation + ")", trace.categories.ViewHierarchy); trace.write(this._owner + " didRotateFromInterfaceOrientation(" + fromInterfaceOrientation + ")", trace.categories.ViewHierarchy);
if (this._owner._isModal) {
var parentBounds = (<any>this._owner)._UIModalPresentationFormSheet ? (<UIView>this._owner._nativeView).superview.bounds : UIScreen.mainScreen().bounds;
uiUtils.ios._layoutRootView(this._owner, parentBounds);
}
} }
public viewDidLoad() { public viewDidLoad() {
trace.write(this._owner + " viewDidLoad", trace.categories.ViewHierarchy); trace.write(this._owner + " viewDidLoad", trace.categories.ViewHierarchy);
this.view.autoresizesSubviews = false;
this.view.autoresizingMask = UIViewAutoresizing.UIViewAutoresizingNone;
} }
public viewDidLayoutSubviews() { public viewDidLayoutSubviews() {
trace.write(this._owner + " viewDidLayoutSubviews, isLoaded = " + this._owner.isLoaded, trace.categories.ViewHierarchy); trace.write(this._owner + " viewDidLayoutSubviews, isLoaded = " + this._owner.isLoaded, trace.categories.ViewHierarchy);
if (!this._owner.isLoaded) {
return;
}
if (this._owner._isModal) { if (this._owner._isModal) {
var parentBounds = (<any>this._owner)._UIModalPresentationFormSheet ? this._owner._nativeView.superview.bounds : UIScreen.mainScreen().bounds; let isTablet = device.deviceType === DeviceType.Tablet;
uiUtils.ios._layoutRootView(this._owner, parentBounds); let isFullScreen = !this._owner._UIModalPresentationFormSheet || !isTablet;
let frame = isFullScreen ? UIScreen.mainScreen().bounds : this.view.frame;
let origin = frame.origin;
let size = frame.size;
let width = size.width;
let height = size.height;
let mode: number = utils.layout.EXACTLY;
let superViewRotationRadians;
if (this.view.superview) {
let transform = this.view.superview.transform;
superViewRotationRadians = atan2f(transform.b, transform.a);
}
if (utils.ios.MajorVersion < 8 && utils.ios.isLandscape() && !superViewRotationRadians) {
// in iOS 7 when in landscape we switch width with height because on device they don't change even when rotated.
width = size.height;
height = size.width;
}
let bottom = height;
let statusBarHeight = uiUtils.ios.getStatusBarHeight();
let statusBarVisible = !UIApplication.sharedApplication().statusBarHidden;
let backgroundSpanUnderStatusBar = this._owner.backgroundSpanUnderStatusBar;
if (statusBarVisible && !backgroundSpanUnderStatusBar) {
height -= statusBarHeight;
}
let widthSpec = utils.layout.makeMeasureSpec(width, mode);
let heightSpec = utils.layout.makeMeasureSpec(height, mode);
View.measureChild(null, this._owner, widthSpec, heightSpec);
let top = ((backgroundSpanUnderStatusBar && isFullScreen) || utils.ios.MajorVersion < 8 || !isFullScreen) ? 0 : statusBarHeight;
View.layoutChild(null, this._owner, 0, top, width, bottom);
if (utils.ios.MajorVersion < 8) {
if (!backgroundSpanUnderStatusBar && (!isTablet || isFullScreen)) {
if (utils.ios.isLandscape() && !superViewRotationRadians) {
this.view.center = CGPointMake(this.view.center.x - statusBarHeight, this.view.center.y);
}
else {
this.view.center = CGPointMake(this.view.center.x, this.view.center.y + statusBarHeight);
}
}
}
trace.write(this._owner + ", native frame = " + NSStringFromCGRect(this.view.frame), trace.categories.Layout);
} }
else { else {
this._owner._updateLayout(); this._owner._updateLayout();
@ -64,6 +110,7 @@ export class Page extends pageCommon.Page {
private _ios: UIViewController; private _ios: UIViewController;
public _enableLoadedEvents: boolean; public _enableLoadedEvents: boolean;
public _isModal: boolean = false; public _isModal: boolean = false;
public _UIModalPresentationFormSheet: boolean = false;
constructor(options?: definition.Options) { constructor(options?: definition.Options) {
super(options); super(options);
@ -139,18 +186,18 @@ export class Page extends pageCommon.Page {
if (fullscreen) { if (fullscreen) {
this._ios.modalPresentationStyle = UIModalPresentationStyle.UIModalPresentationFullScreen; this._ios.modalPresentationStyle = UIModalPresentationStyle.UIModalPresentationFullScreen;
uiUtils.ios._layoutRootView(this, UIScreen.mainScreen().bounds); //uiUtils.ios._layoutRootView(this, UIScreen.mainScreen().bounds);
} }
else { else {
this._ios.modalPresentationStyle = UIModalPresentationStyle.UIModalPresentationFormSheet; this._ios.modalPresentationStyle = UIModalPresentationStyle.UIModalPresentationFormSheet;
(<any>this)._UIModalPresentationFormSheet = true; this._UIModalPresentationFormSheet = true;
} }
var that = this; var that = this;
parent.ios.presentViewControllerAnimatedCompletion(this._ios, false, function completion() { parent.ios.presentViewControllerAnimatedCompletion(this._ios, false, function completion() {
if (!fullscreen) { if (!fullscreen) {
// We can measure and layout the modal page after we know its parent's dimensions. // We can measure and layout the modal page after we know its parent's dimensions.
uiUtils.ios._layoutRootView(that, that._nativeView.superview.bounds); //uiUtils.ios._layoutRootView(that, that._nativeView.superview.bounds);
} }
that._raiseShownModallyEvent(parent, context, closeCallback); that._raiseShownModallyEvent(parent, context, closeCallback);
@ -158,9 +205,10 @@ export class Page extends pageCommon.Page {
} }
protected _hideNativeModalView(parent: Page) { protected _hideNativeModalView(parent: Page) {
parent._ios.dismissModalViewControllerAnimated(false);
this._isModal = false; this._isModal = false;
(<any>this)._UIModalPresentationFormSheet = false; this._UIModalPresentationFormSheet = false;
parent.requestLayout();
parent._ios.dismissModalViewControllerAnimated(false);
} }
public _updateActionBar(hidden: boolean) { public _updateActionBar(hidden: boolean) {
@ -180,6 +228,15 @@ export class Page extends pageCommon.Page {
let actionBarWidth: number = 0; let actionBarWidth: number = 0;
let actionBarHeight: number = 0; let actionBarHeight: number = 0;
// If background span under statusbar reduce available height for page content.
let statusBarHeight = this.backgroundSpanUnderStatusBar ? uiUtils.ios.getStatusBarHeight() : 0;
// Phones does not support fullScreen=false for modal pages so we reduce statusbar only when on tablet and not in fullscreen
if (this._isModal && this._UIModalPresentationFormSheet && device.deviceType === DeviceType.Tablet) {
statusBarHeight = 0;
}
if (this.frame && this.frame._getNavBarVisible(this)) { if (this.frame && this.frame._getNavBarVisible(this)) {
// Measure ActionBar with the full height. // Measure ActionBar with the full height.
let actionBarSize = View.measureChild(this, this.actionBar, widthMeasureSpec, heightMeasureSpec); let actionBarSize = View.measureChild(this, this.actionBar, widthMeasureSpec, heightMeasureSpec);
@ -187,7 +244,7 @@ export class Page extends pageCommon.Page {
actionBarHeight = actionBarSize.measuredHeight; actionBarHeight = actionBarSize.measuredHeight;
} }
let heightSpec = utils.layout.makeMeasureSpec(height - actionBarHeight, heightMode); let heightSpec = utils.layout.makeMeasureSpec(height - actionBarHeight - statusBarHeight, heightMode);
// Measure content with height - navigationBarHeight. Here we could use actionBarSize.measuredHeight probably. // Measure content with height - navigationBarHeight. Here we could use actionBarSize.measuredHeight probably.
let result = View.measureChild(this, this.content, widthMeasureSpec, heightSpec); let result = View.measureChild(this, this.content, widthMeasureSpec, heightSpec);
@ -204,8 +261,18 @@ export class Page extends pageCommon.Page {
public onLayout(left: number, top: number, right: number, bottom: number) { public onLayout(left: number, top: number, right: number, bottom: number) {
View.layoutChild(this, this.actionBar, 0, 0, right - left, bottom - top); View.layoutChild(this, this.actionBar, 0, 0, right - left, bottom - top);
let navigationBarHeight = this.frame ? this.frame.navigationBarHeight : 0; let navigationBarHeight: number = 0;
View.layoutChild(this, this.content, 0, navigationBarHeight, right - left, bottom - top); if (this.frame && this.frame._getNavBarVisible(this)) {
navigationBarHeight = this.actionBar.getMeasuredHeight();
}
let statusBarHeight = this.backgroundSpanUnderStatusBar ? uiUtils.ios.getStatusBarHeight() : 0;
// Phones does not support fullScreen=false for modal pages so we reduce statusbar only when on tablet and not in fullscreen
if (this._isModal && this._UIModalPresentationFormSheet && device.deviceType === DeviceType.Tablet) {
statusBarHeight = 0;
}
View.layoutChild(this, this.content, 0, navigationBarHeight + statusBarHeight, right - left, bottom - top);
} }
public _addViewToNativeVisualTree(view: View): boolean { public _addViewToNativeVisualTree(view: View): boolean {

View File

@ -254,8 +254,9 @@ export class TabView extends common.TabView {
var height = utils.layout.getMeasureSpecSize(heightMeasureSpec); var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
this._tabBarHeight = uiUtils.ios.getActualHeight(this._ios.tabBar); this._tabBarHeight = TabView.measureHelper(this._ios.tabBar, width, widthMode, height, heightMode).height;
this._navBarHeight = uiUtils.ios.getActualHeight(this._ios.moreNavigationController.navigationBar); 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 density = utils.layout.getDisplayDensity();
var measureWidth = 0; var measureWidth = 0;
@ -263,7 +264,7 @@ export class TabView extends common.TabView {
var child = this._selectedView; var child = this._selectedView;
if (child) { if (child) {
var childHeightMeasureSpec = utils.layout.makeMeasureSpec(height - (this._navBarHeight + this._tabBarHeight), heightMode); var childHeightMeasureSpec = utils.layout.makeMeasureSpec(height - this._navBarHeight - this._tabBarHeight, heightMode);
var childSize = view.View.measureChild(this, child, widthMeasureSpec, childHeightMeasureSpec); var childSize = view.View.measureChild(this, child, widthMeasureSpec, childHeightMeasureSpec);
measureHeight = childSize.measuredHeight; measureHeight = childSize.measuredHeight;
@ -289,4 +290,9 @@ export class TabView extends common.TabView {
} }
} }
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));
}
} }

2
ui/utils.d.ts vendored
View File

@ -8,5 +8,7 @@
export function getActualHeight(uiView: UIView): number; export function getActualHeight(uiView: UIView): number;
export function _layoutRootView(rootView: view.View, parentBounds: CGRect): void; export function _layoutRootView(rootView: view.View, parentBounds: CGRect): void;
export function getStatusBarHeight(): number;
} }
} }

View File

@ -1,76 +1,52 @@
import view = require("ui/core/view"); import {View} from "ui/core/view";
import utils = require("utils/utils"); import utils = require("utils/utils");
export module ios { export module ios {
export function getActualHeight(uiView: UIView): number { export function getActualHeight(view: UIView): number {
if (uiView.window && !uiView.hidden) { if (view.window && !view.hidden) {
return uiView.frame.size.height; return view.frame.size.height;
} }
return 0; return 0;
} }
export function _layoutRootView(rootView: view.View, parentBounds: CGRect) { export function getStatusBarHeight(): number {
var app = UIApplication.sharedApplication();
if (!app || app.statusBarHidden) {
return 0;
}
var statusFrame = app.statusBarFrame;
return Math.min(statusFrame.size.width, statusFrame.size.height);
}
export function _layoutRootView(rootView: View, parentBounds: CGRect) {
if (!rootView || !parentBounds) { if (!rootView || !parentBounds) {
return; return;
} }
var landscape = utils.ios.isLandscape(); let size = parentBounds.size;
var iOSMajorVersion = utils.ios.MajorVersion; let width = size.width;
var size = parentBounds.size; let height = size.height;
var width = size.width;
var height = size.height;
//trace.write("--------------------------------------------", "LayoutRootView.iOS");
//trace.write("| Layout Root View", "LayoutRootView.iOS");
//trace.write("| rootView: " + rootView, "LayoutRootView.iOS");
//trace.write("| parentBounds: " + NSStringFromCGRect(parentBounds), "LayoutRootView.iOS");
//trace.write("| UIScreen.mainScreen().bounds: " + NSStringFromCGRect(UIScreen.mainScreen().bounds), "LayoutRootView.iOS");
//trace.write("| _isModal: " + (<any>rootView)._isModal, "LayoutRootView.iOS");
//trace.write("| _UIModalPresentationFormSheet: " + (<any>rootView)._UIModalPresentationFormSheet, "LayoutRootView.iOS");
//trace.write("| landscape: " + landscape, "LayoutRootView.iOS");
//trace.write("| iOSMajorVersion: " + iOSMajorVersion, "LayoutRootView.iOS");
var superview = (<UIView>rootView._nativeView).superview; var superview = (<UIView>rootView._nativeView).superview;
//trace.write("| superview: " + superview, "LayoutRootView.iOS");
var superViewRotationRadians; var superViewRotationRadians;
if (superview) { if (superview) {
superViewRotationRadians = atan2f(superview.transform.b, superview.transform.a); superViewRotationRadians = atan2f(superview.transform.b, superview.transform.a);
//trace.write("| superViewRotationRadians: " + superViewRotationRadians + " rad.", "LayoutRootView.iOS");
//trace.write("| superview.bounds: " + NSStringFromCGRect(superview.bounds), "LayoutRootView.iOS");
} }
if (iOSMajorVersion < 8 && landscape && !superViewRotationRadians) { if (utils.ios.MajorVersion < 8 && utils.ios.isLandscape() && !superViewRotationRadians) {
// in iOS 7 when in landscape we switch width with height because on device they don't change even when rotated. // in iOS 7 when in landscape we switch width with height because on device they don't change even when rotated.
//trace.write("| >>> Detected iOS 7 device in landscape mode and superview is not rotated. Manually swapping width and height...", "LayoutRootView.iOS");
width = size.height; width = size.height;
height = size.width; height = size.width;
} }
var statusBarHeight;
if (UIApplication.sharedApplication().statusBarHidden || ((<any>rootView)._UIModalPresentationFormSheet && !CGSizeEqualToSize(parentBounds.size, UIScreen.mainScreen().bounds.size))) {
statusBarHeight = 0;
}
else {
// Status bar section
var statusFrame = UIApplication.sharedApplication().statusBarFrame;
try {
statusBarHeight = Math.min(statusFrame.size.width, statusFrame.size.height);
} catch (ex) {
console.log("exception: " + ex);
}
}
//trace.write("| UIApplication.sharedApplication().statusBarHidden: " + UIApplication.sharedApplication().statusBarHidden, "LayoutRootView.iOS");
//trace.write("| statusBarHeight: " + statusBarHeight, "LayoutRootView.iOS");
var origin = parentBounds.origin; var origin = parentBounds.origin;
var left = origin.x; var left = origin.x;
var top = origin.y + statusBarHeight; var top = origin.y;
var widthSpec = utils.layout.makeMeasureSpec(width, utils.layout.EXACTLY); var widthSpec = utils.layout.makeMeasureSpec(width, utils.layout.EXACTLY);
var heightSpec = utils.layout.makeMeasureSpec(height - statusBarHeight, utils.layout.EXACTLY); var heightSpec = utils.layout.makeMeasureSpec(height, utils.layout.EXACTLY);
//trace.write("| >>> Will measure and layout with {{" + left + ", " + top + "}{" + width + ", " + height + "}}", "LayoutRootView.iOS");
//trace.write("--------------------------------------------", "LayoutRootView.iOS");
rootView.measure(widthSpec, heightSpec); rootView.measure(widthSpec, heightSpec);
rootView.layout(left, top, width, height); rootView.layout(left, top, width, height);