Generated platforms declarations for iOS 11

Fixed broken unit-tests
ios layout now run only for the innermost viewcontoller
ios layout for viewcontrollers now implemented with constraints
Added ios11 safeAreas layout support
onMeasure back for frame and tab-view so that they won't throw exception if measure is called on them
Page parents layout updated after page is layout so that inner layout flags are correct
This commit is contained in:
Hristo Hristov
2017-10-06 09:57:29 +03:00
parent 38d026a3df
commit 199d83c902
18 changed files with 479 additions and 322 deletions

View File

@ -356,11 +356,9 @@ export function waitUntilReady(isReady: () => boolean, timeoutSec: number = 3, s
} }
if (Application.ios) { if (Application.ios) {
// const waitTime = 1 / 10000;
const timeoutMs = timeoutSec * 1000; const timeoutMs = timeoutSec * 1000;
let totalWaitTime = 0; let totalWaitTime = 0;
while (true) { while (true) {
// const nsDate = <any>NSDate.dateWithTimeIntervalSinceNow(waitTime);
const begin = time(); const begin = time();
const currentRunLoop = utils.ios.getter(NSRunLoop, NSRunLoop.currentRunLoop); const currentRunLoop = utils.ios.getter(NSRunLoop, NSRunLoop.currentRunLoop);
currentRunLoop.limitDateForMode(currentRunLoop.currentMode); currentRunLoop.limitDateForMode(currentRunLoop.currentMode);

View File

@ -15,6 +15,7 @@ exports.pageLoaded = pageLoaded;
// << article-set-bindingcontext // << article-set-bindingcontext
import * as TKUnit from "../../TKUnit"; import * as TKUnit from "../../TKUnit";
import * as helper from "../helper"; import * as helper from "../helper";
import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout";
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout"; import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
import { View, PercentLength, Observable, unsetValue, EventData, isIOS } from "tns-core-modules/ui/core/view"; import { View, PercentLength, Observable, unsetValue, EventData, isIOS } from "tns-core-modules/ui/core/view";
import { Label } from "tns-core-modules/ui/label"; import { Label } from "tns-core-modules/ui/label";
@ -562,31 +563,40 @@ export function test_percent_width_and_height_support() {
export function test_percent_margin_support() { export function test_percent_margin_support() {
const testPage = new Page(); const testPage = new Page();
testPage.id = "ttest_percent_margin_support"; const gridLayout = new GridLayout();
const stackLayout = new StackLayout(); const stackLayout = new StackLayout();
stackLayout.margin = "10%"; stackLayout.margin = "10%";
testPage.content = stackLayout; gridLayout.addChild(stackLayout);
testPage.content = gridLayout;
const pageWidth = testPage.getMeasuredWidth(); helper.navigate(() => testPage);
const pageHeight = testPage.getMeasuredHeight()
const marginLeft = pageWidth * 0.1; const parentBounds = gridLayout._getCurrentLayoutBounds();
const marginTop = pageHeight * 0.1; const parentWidth = parentBounds.right - parentBounds.left;
const parentHeight = parentBounds.bottom - parentBounds.top;
const bounds = stackLayout._getCurrentLayoutBounds(); const marginLeft = Math.round(parentWidth * 0.1);
TKUnit.assertEqual(bounds.left, Math.round(marginLeft), "Page's content LEFT position incorrect"); const marginTop = Math.round(parentHeight * 0.1);
TKUnit.assertEqual(bounds.top, Math.round(marginTop), "Page's content TOP position incorrect");
TKUnit.assertEqual(bounds.right, Math.round(marginLeft + pageWidth), "Page's content RIGHT position incorrect"); let bounds = stackLayout._getCurrentLayoutBounds();
TKUnit.assertEqual(bounds.bottom, Math.round(marginTop + pageHeight), "Page's content BOTTOM position incorrect"); TKUnit.assertEqual(Math.round(bounds.left), marginLeft, "Stack LEFT position incorrect");
TKUnit.assertEqual(Math.round(bounds.top), marginTop, "Stack TOP position incorrect");
TKUnit.assertEqual(Math.round(bounds.bottom - bounds.top), parentHeight - (2 * marginTop), "Stack HEIGHT incorrect");
TKUnit.assertEqual(Math.round(bounds.right - bounds.left), parentWidth - (2 * marginLeft), "Stack WIDTH incorrect");
TKUnit.assertEqual(Math.round(bounds.right), parentWidth - marginLeft, "Stack RIGHT position incorrect");
TKUnit.assertEqual(Math.round(bounds.bottom), parentHeight - marginTop, "Stack BOTTOM position incorrect");
//reset values. //reset values.
testPage.margin = "0"; stackLayout.margin = "0";
TKUnit.waitUntilReady(() => stackLayout.isLayoutValid);
TKUnit.assertTrue(PercentLength.equals(testPage.marginLeft, 0)); bounds = stackLayout._getCurrentLayoutBounds();
TKUnit.assertTrue(PercentLength.equals(testPage.marginTop, 0)); TKUnit.assertEqual(bounds.left, 0, "Stack LEFT position incorrect");
TKUnit.assertTrue(PercentLength.equals(testPage.marginRight, 0)); TKUnit.assertEqual(bounds.top, 0, "Stack TOP position incorrect");
TKUnit.assertTrue(PercentLength.equals(testPage.marginBottom, 0)); TKUnit.assertEqual(bounds.bottom - bounds.top, parentHeight, "Stack HEIGHT incorrect");
TKUnit.assertEqual(bounds.right - bounds.left, parentWidth, "Stack WIDTH incorrect");
TKUnit.assertEqual(bounds.right, parentWidth, "Stack RIGHT position incorrect");
TKUnit.assertEqual(bounds.bottom, parentHeight, "Stack BOTTOM position incorrect");
} }
//export function test_ModalPage_Layout_is_Correct() { //export function test_ModalPage_Layout_is_Correct() {

View File

@ -1,5 +1,6 @@
import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view"; import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view";
import { Page, layout, View, EventData } from "tns-core-modules/ui/page"; import { Page, layout, View, EventData } from "tns-core-modules/ui/page";
import { ios as iosView } from "tns-core-modules/ui/core/view";
import { Label } from "tns-core-modules/ui/label"; import { Label } from "tns-core-modules/ui/label";
import { topmost } from "tns-core-modules/ui/frame"; import { topmost } from "tns-core-modules/ui/frame";
import * as uiUtils from "tns-core-modules/ui/utils"; import * as uiUtils from "tns-core-modules/ui/utils";
@ -72,6 +73,11 @@ function getHeight(view: View): number {
return bounds.bottom - bounds.top; return bounds.bottom - bounds.top;
} }
function getNativeHeight(view: View): number {
const bounds = view.nativeViewProtected.frame;
return layout.toDevicePixels(bounds.size.height);
}
export function test_correct_layout_scrollable_content_false() { export function test_correct_layout_scrollable_content_false() {
const page = new Page(); const page = new Page();
(<any>page).scrollableContent = false; (<any>page).scrollableContent = false;
@ -82,6 +88,7 @@ export function test_correct_layout_scrollable_content_false() {
const tabItem = new TabViewItem(); const tabItem = new TabViewItem();
tabItem.title = "Item"; tabItem.title = "Item";
const lbl = new Label(); const lbl = new Label();
(<any>lbl).scrollableContent = false;
tabItem.view = lbl; tabItem.view = lbl;
tabView.items = [tabItem]; tabView.items = [tabItem];
@ -94,24 +101,28 @@ export function test_correct_layout_scrollable_content_false() {
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height); const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
let pageHeight = getHeight(page); let pageHeight = getHeight(page);
let expectedPageHeight = screenHeight - statusBarHeight; let expectedPageHeight = screenHeight;
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar"); TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight");
let contentHeight = getHeight(lbl); let contentHeight = getHeight(lbl);
let contentNativeHeight = getNativeHeight(lbl);
let expectedLabelHeight = screenHeight - statusBarHeight - tabBarHeight; let expectedLabelHeight = screenHeight - statusBarHeight - tabBarHeight;
TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar - tabBar"); TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar - tabBar");
TKUnit.assertEqual(contentNativeHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar - tabBar");
page.actionBarHidden = false; page.actionBarHidden = false;
TKUnit.waitUntilReady(() => page.isLayoutValid); TKUnit.waitUntilReady(() => page.isLayoutValid);
pageHeight = getHeight(page); pageHeight = getHeight(page);
const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar); const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar);
expectedPageHeight = screenHeight - statusBarHeight - navBarHeight; expectedPageHeight = screenHeight;
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar - navBarHeight"); TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight");
contentHeight = getHeight(lbl); contentHeight = getHeight(lbl);
contentNativeHeight = getNativeHeight(lbl);
expectedLabelHeight = screenHeight - statusBarHeight - tabBarHeight - navBarHeight; expectedLabelHeight = screenHeight - statusBarHeight - tabBarHeight - navBarHeight;
TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBarHeight - tabBarHeight - navBarHeight"); TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBarHeight - tabBarHeight - navBarHeight");
TKUnit.assertEqual(contentNativeHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBarHeight - tabBarHeight - navBarHeight");
} }
export function test_correct_layout_scrollable_content_true() { export function test_correct_layout_scrollable_content_true() {
@ -137,12 +148,12 @@ export function test_correct_layout_scrollable_content_true() {
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height); const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
let pageHeight = getHeight(page); let pageHeight = getHeight(page);
let expectedPageHeight = screenHeight - statusBarHeight; let expectedPageHeight = screenHeight;
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar"); TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight");
let contentHeight = getHeight(lbl); let contentHeight = getHeight(lbl);
let expectedLabelHeight = screenHeight - statusBarHeight; let expectedLabelHeight = screenHeight - statusBarHeight;
TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar - tabBar"); TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar");
page.actionBarHidden = false; page.actionBarHidden = false;
TKUnit.waitUntilReady(() => page.isLayoutValid); TKUnit.waitUntilReady(() => page.isLayoutValid);

View File

@ -1,26 +1,26 @@
import * as TKUnit from "../../TKUnit"; import * as TKUnit from "../../TKUnit";
import * as app from "tns-core-modules/application"; import { Button } from "tns-core-modules/ui/button";
import * as button from "tns-core-modules/ui/button"; import { Page, isIOS } from "tns-core-modules/ui/page";
import * as testModule from "../../ui-test"; import { UITest } from "../../ui-test";
import * as layoutHelper from "../layouts/layout-helper"; import * as layoutHelper from "../layouts/layout-helper";
import { Page } from "tns-core-modules/ui/page"; import { Page } from "tns-core-modules/ui/page";
import * as frame from "tns-core-modules/ui/frame"; import * as frame from "tns-core-modules/ui/frame";
import * as helper from "../helper"; import * as helper from "../helper";
// >> article-require-scrollview-module // >> article-require-scrollview-module
import * as scrollViewModule from "tns-core-modules/ui/scroll-view"; import { ScrollView, ScrollEventData } from "tns-core-modules/ui/scroll-view";
// << article-require-scrollview-module // << article-require-scrollview-module
class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> { class ScrollLayoutTest extends UITest<ScrollView> {
public create(): scrollViewModule.ScrollView { public create(): ScrollView {
let scrollView = new scrollViewModule.ScrollView(); const scrollView = new ScrollView();
scrollView.orientation = "vertical"; scrollView.orientation = "vertical";
scrollView.width = { value: 200, unit: "px" }; scrollView.width = { value: 200, unit: "px" };
scrollView.height = { value: 300, unit: "px" }; scrollView.height = { value: 300, unit: "px" };
let btn = new button.Button(); const btn = new Button();
btn.text = "test"; btn.text = "test";
btn.width = { value: 500, unit: "px" }; btn.width = { value: 500, unit: "px" };
btn.height = { value: 500, unit: "px" }; btn.height = { value: 500, unit: "px" };
@ -31,13 +31,13 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
public test_snippets() { public test_snippets() {
// >> article-creating-scrollview // >> article-creating-scrollview
var scrollView = new scrollViewModule.ScrollView(); const scrollView = new ScrollView();
// << article-creating-scrollview // << article-creating-scrollview
TKUnit.assertTrue(scrollView !== null, "ScrollView should be created."); TKUnit.assertTrue(scrollView !== null, "ScrollView should be created.");
} }
public test_default_TNS_values() { public test_default_TNS_values() {
let scroll = new scrollViewModule.ScrollView(); const scroll = new ScrollView();
TKUnit.assertEqual(scroll.orientation, "vertical", "Default this.testView.orientation"); TKUnit.assertEqual(scroll.orientation, "vertical", "Default this.testView.orientation");
TKUnit.assertEqual(scroll.verticalOffset, 0, "Default this.testView.verticalOffset"); TKUnit.assertEqual(scroll.verticalOffset, 0, "Default this.testView.verticalOffset");
TKUnit.assertEqual(scroll.horizontalOffset, 0, "Default this.testView.horizontalOffset"); TKUnit.assertEqual(scroll.horizontalOffset, 0, "Default this.testView.horizontalOffset");
@ -46,22 +46,20 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
public test_vertical_oriantation_creates_correct_native_view() { public test_vertical_oriantation_creates_correct_native_view() {
this.testView.orientation = "vertical"; this.testView.orientation = "vertical";
if (app.android) { if (isIOS) {
TKUnit.assert(this.testView.android instanceof org.nativescript.widgets.VerticalScrollView, "android property should be instanceof org.nativescript.widgets.VerticalScrollView");
}
else if (app.ios) {
TKUnit.assert(this.testView.ios instanceof UIScrollView, "ios property is UIScrollView"); TKUnit.assert(this.testView.ios instanceof UIScrollView, "ios property is UIScrollView");
} else {
TKUnit.assert(this.testView.android instanceof org.nativescript.widgets.VerticalScrollView, "android property should be instanceof org.nativescript.widgets.VerticalScrollView");
} }
} }
public test_horizontal_oriantation_creates_correct_native_view() { public test_horizontal_oriantation_creates_correct_native_view() {
this.testView.orientation = "horizontal"; this.testView.orientation = "horizontal";
if (app.android) { if (isIOS) {
TKUnit.assert(this.testView.android instanceof org.nativescript.widgets.HorizontalScrollView, "android property should be instanceof org.nativescript.widgets.HorizontalScrollView");
}
else if (app.ios) {
TKUnit.assert(this.testView.ios instanceof UIScrollView, "ios property is UIScrollView"); TKUnit.assert(this.testView.ios instanceof UIScrollView, "ios property is UIScrollView");
} else {
TKUnit.assert(this.testView.android instanceof org.nativescript.widgets.HorizontalScrollView, "android property should be instanceof org.nativescript.widgets.HorizontalScrollView");
} }
} }
@ -102,28 +100,18 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
} }
public test_scrollToVerticalOffset_no_animation() { public test_scrollToVerticalOffset_no_animation() {
TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
// NOTE: when automaticallyAdjustsScrollViewInsets is true (which is the default value)
// ScrollView verticalOffset is 20.
// TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
this.testView.scrollToVerticalOffset(layoutHelper.dp(100), false); this.testView.scrollToVerticalOffset(layoutHelper.dp(100), false);
TKUnit.assertAreClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.1, "this.testView.verticalOffset"); TKUnit.assertAreClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.1, "this.testView.verticalOffset");
} }
public test_scrollToVerticalOffset_with_animation() { public test_scrollToVerticalOffset_with_animation() {
TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
// NOTE: when automaticallyAdjustsScrollViewInsets is true (which is the default value)
// ScrollView verticalOffset is 20.
// TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
this.testView.scrollToVerticalOffset(layoutHelper.dp(100), true); this.testView.scrollToVerticalOffset(layoutHelper.dp(100), true);
// No synchronous change.
// NOTE: when automaticallyAdjustsScrollViewInsets is true (which is the default value)
// ScrollView verticalOffset is 20.
// TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
TKUnit.waitUntilReady(() => { return TKUnit.areClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.9); }); TKUnit.waitUntilReady(() => { return TKUnit.areClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.9); });
// The scrolling animation should be finished by now // The scrolling animation should be finished by now
@ -158,17 +146,15 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
public test_scrollView_persistsState_vertical() { public test_scrollView_persistsState_vertical() {
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
this.testView.scrollToVerticalOffset(layoutHelper.dp(100), false); const expected = layoutHelper.dp(100);
TKUnit.assertAreClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.1, "this.testView.verticalOffset before navigation"); this.testView.scrollToVerticalOffset(expected, false);
// TKUnit.assertAreClose(this.testView.verticalOffset, expected, 0.1, "this.testView.verticalOffset before navigation");
helper.navigateWithHistory(() => new Page()); helper.navigateWithHistory(() => new Page());
helper.goBack(); helper.goBack();
// Wait for the page to reload.
TKUnit.waitUntilReady(() => { return TKUnit.areClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.1); });
// Check verticalOffset after navigation // Check verticalOffset after navigation
TKUnit.assertAreClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.1, "this.testView.verticalOffset after navigation"); TKUnit.assertAreClose(this.testView.verticalOffset, expected, 0.1, "this.testView.verticalOffset after navigation");
} }
public test_scrollView_persistsState_horizontal() { public test_scrollView_persistsState_horizontal() {
@ -178,16 +164,17 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.testView.scrollToHorizontalOffset(layoutHelper.dp(100), false); this.testView.scrollToHorizontalOffset(layoutHelper.dp(100), false);
TKUnit.assertAreClose(layoutHelper.dip(this.testView.horizontalOffset), 100, 0.1, "this.testView.horizontalOffset before navigation"); TKUnit.assertAreClose(layoutHelper.dip(this.testView.horizontalOffset), 100, 0.1, "this.testView.horizontalOffset before navigation");
helper.navigateWithHistory(() => new Page()); helper.navigateWithHistory(() => new Page());
helper.goBack(); helper.goBack();
// Check verticalOffset after navigation // Check horizontalOffset after navigation
TKUnit.assertAreClose(layoutHelper.dip(this.testView.horizontalOffset), 100, 0.1, "this.testView.horizontalOffset after navigation"); TKUnit.assertAreClose(layoutHelper.dip(this.testView.horizontalOffset), 100, 0.1, "this.testView.horizontalOffset after navigation");
} }
public test_scrollView_vertical_raised_scroll_event() { public test_scrollView_vertical_raised_scroll_event() {
var scrollY: number; var scrollY: number;
this.testView.on(scrollViewModule.ScrollView.scrollEvent, (args: scrollViewModule.ScrollEventData) => { this.testView.on(ScrollView.scrollEvent, (args: ScrollEventData) => {
scrollY = args.scrollY; scrollY = args.scrollY;
}); });
@ -202,7 +189,7 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.testView.orientation = "horizontal"; this.testView.orientation = "horizontal";
var scrollX: number; var scrollX: number;
this.testView.on(scrollViewModule.ScrollView.scrollEvent, (args: scrollViewModule.ScrollEventData) => { this.testView.on(ScrollView.scrollEvent, (args: ScrollEventData) => {
scrollX = args.scrollX; scrollX = args.scrollX;
}); });
@ -217,7 +204,7 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
var scrollY: number; var scrollY: number;
this.testView.on(scrollViewModule.ScrollView.scrollEvent, (args: scrollViewModule.ScrollEventData) => { this.testView.on(ScrollView.scrollEvent, (args: ScrollEventData) => {
scrollY = args.scrollY; scrollY = args.scrollY;
}); });
@ -232,7 +219,7 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
var scrollX: number; var scrollX: number;
this.testView.on(scrollViewModule.ScrollView.scrollEvent, (args: scrollViewModule.ScrollEventData) => { this.testView.on(ScrollView.scrollEvent, (args: ScrollEventData) => {
scrollX = args.scrollX; scrollX = args.scrollX;
}); });
@ -249,7 +236,7 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.testView.scrollBarIndicatorVisible = true; this.testView.scrollBarIndicatorVisible = true;
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
if (app.ios) { if (isIOS) {
TKUnit.assertEqual(this.testView.ios.showsHorizontalScrollIndicator, true); TKUnit.assertEqual(this.testView.ios.showsHorizontalScrollIndicator, true);
} else { } else {
TKUnit.assertEqual(this.testView.android.isHorizontalScrollBarEnabled(), true); TKUnit.assertEqual(this.testView.android.isHorizontalScrollBarEnabled(), true);
@ -258,7 +245,7 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.testView.scrollBarIndicatorVisible = false; this.testView.scrollBarIndicatorVisible = false;
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
if (app.ios) { if (isIOS) {
TKUnit.assertEqual(this.testView.ios.showsHorizontalScrollIndicator, false); TKUnit.assertEqual(this.testView.ios.showsHorizontalScrollIndicator, false);
} else { } else {
TKUnit.assertEqual(this.testView.android.isHorizontalScrollBarEnabled(), false); TKUnit.assertEqual(this.testView.android.isHorizontalScrollBarEnabled(), false);
@ -270,7 +257,7 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.testView.scrollBarIndicatorVisible = true; this.testView.scrollBarIndicatorVisible = true;
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
if (app.ios) { if (isIOS) {
TKUnit.assertEqual(this.testView.ios.showsVerticalScrollIndicator, true); TKUnit.assertEqual(this.testView.ios.showsVerticalScrollIndicator, true);
} else { } else {
TKUnit.assertEqual(this.testView.android.isVerticalScrollBarEnabled(), true); TKUnit.assertEqual(this.testView.android.isVerticalScrollBarEnabled(), true);
@ -279,7 +266,7 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
this.testView.scrollBarIndicatorVisible = false; this.testView.scrollBarIndicatorVisible = false;
this.waitUntilTestElementLayoutIsValid(); this.waitUntilTestElementLayoutIsValid();
if (app.ios) { if (isIOS) {
TKUnit.assertEqual(this.testView.ios.showsVerticalScrollIndicator, false); TKUnit.assertEqual(this.testView.ios.showsVerticalScrollIndicator, false);
} else { } else {
TKUnit.assertEqual(this.testView.android.isVerticalScrollBarEnabled(), false); TKUnit.assertEqual(this.testView.android.isVerticalScrollBarEnabled(), false);

View File

@ -246,7 +246,7 @@ function _clickTheFirstButtonInTheListViewNatively(tabView: TabView) {
button.performClick(); button.performClick();
} }
else { else {
const tableView = <UITableView>tabView.ios.viewControllers[0].view; const tableView = <UITableView>tabView.ios.selectedViewController.view.subviews[0];
const cell = <UITableViewCell>tableView.cellForRowAtIndexPath(NSIndexPath.indexPathForItemInSection(0, 0)); const cell = <UITableViewCell>tableView.cellForRowAtIndexPath(NSIndexPath.indexPathForItemInSection(0, 0));
const btn = <UIButton>cell.contentView.subviews[0]; const btn = <UIButton>cell.contentView.subviews[0];
btn.sendActionsForControlEvents(UIControlEvents.TouchUpInside); btn.sendActionsForControlEvents(UIControlEvents.TouchUpInside);

View File

@ -14,7 +14,7 @@ import {
// First reexport so that app module is initialized. // First reexport so that app module is initialized.
export * from "./application-common"; export * from "./application-common";
import { ios as iosView } from "../ui/core/view"; import { ios as iosView, ViewBase } from "../ui/core/view";
import { Frame, View, NavigationEntry, loadViewFromEntry } from "../ui/frame"; import { Frame, View, NavigationEntry, loadViewFromEntry } from "../ui/frame";
import { ios } from "../ui/utils"; import { ios } from "../ui/utils";
import * as utils from "../utils/utils"; import * as utils from "../utils/utils";
@ -49,6 +49,7 @@ class IOSApplication implements IOSApplicationDefinition {
private _currentOrientation = utils.ios.getter(UIDevice, UIDevice.currentDevice).orientation; private _currentOrientation = utils.ios.getter(UIDevice, UIDevice.currentDevice).orientation;
private _window: UIWindow; private _window: UIWindow;
private _observers: Array<NotificationObserver>; private _observers: Array<NotificationObserver>;
private _rootView: ViewBase;
constructor() { constructor() {
this._observers = new Array<NotificationObserver>(); this._observers = new Array<NotificationObserver>();
@ -112,6 +113,7 @@ class IOSApplication implements IOSApplicationDefinition {
notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: <any>this, cssFile: getCssFileName() }); notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: <any>this, cssFile: getCssFileName() });
const rootView = createRootView(args.root); const rootView = createRootView(args.root);
this._rootView = rootView;
const controller = getViewController(rootView); const controller = getViewController(rootView);
this._window.rootViewController = controller; this._window.rootViewController = controller;
this._window.makeKeyAndVisible(); this._window.makeKeyAndVisible();
@ -238,12 +240,12 @@ export function getNativeApplication(): UIApplication {
} }
function getViewController(view: View): UIViewController { function getViewController(view: View): UIViewController {
let viewController = view.viewController || view.ios; let viewController: UIViewController = view.viewController || view.ios;
if (viewController instanceof UIViewController) { if (viewController instanceof UIViewController) {
return viewController; return viewController;
} else if (view.ios instanceof UIView) { } else if (view.ios instanceof UIView) {
viewController = iosView.UILayoutViewController.initWithOwner(new WeakRef(view)); viewController = iosView.UILayoutViewController.initWithOwner(new WeakRef(view));
viewController.view = view.ios; viewController.view.addSubview(view.ios);
return viewController; return viewController;
} else { } else {
throw new Error("Root should be either UIViewController or UIView"); throw new Error("Root should be either UIViewController or UIView");

View File

@ -43,11 +43,7 @@ export class ContentView extends CustomLayoutView implements ContentViewDefiniti
} }
get _childrenCount(): number { get _childrenCount(): number {
if (this._content) { return this._content ? 1 : 0;
return 1;
}
return 0;
} }
public _onContentChanged(oldView: View, newView: View) { public _onContentChanged(oldView: View, newView: View) {

View File

@ -122,6 +122,10 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
const changed: boolean = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value; const changed: boolean = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value;
if (wrapped || changed) { if (wrapped || changed) {
if (affectsLayout) {
this.requestLayout();
}
if (reset) { if (reset) {
delete this[key]; delete this[key];
if (valueChanged) { if (valueChanged) {
@ -164,10 +168,6 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue }); this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue });
} }
if (affectsLayout) {
this.requestLayout();
}
if (this.domNode) { if (this.domNode) {
if (reset) { if (reset) {
this.domNode.attributeRemoved(propertyName); this.domNode.attributeRemoved(propertyName);

View File

@ -487,7 +487,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return curState | newState; return curState | newState;
} }
public static layoutChild(parent: ViewDefinition, child: ViewDefinition, left: number, top: number, right: number, bottom: number): void { public static layoutChild(parent: ViewDefinition, child: ViewDefinition, left: number, top: number, right: number, bottom: number, setFrame: boolean = true): void {
if (!child || child.isCollapsed) { if (!child || child.isCollapsed) {
return; return;
} }
@ -571,7 +571,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
traceWrite(child.parent + " :layoutChild: " + child + " " + childLeft + ", " + childTop + ", " + childRight + ", " + childBottom, traceCategories.Layout); traceWrite(child.parent + " :layoutChild: " + child + " " + childLeft + ", " + childTop + ", " + childRight + ", " + childBottom, traceCategories.Layout);
} }
child.layout(childLeft, childTop, childRight, childBottom); child.layout(childLeft, childTop, childRight, childBottom, setFrame);
} }
public static measureChild(parent: ViewCommon, child: ViewCommon, widthMeasureSpec: number, heightMeasureSpec: number): { measuredWidth: number; measuredHeight: number } { public static measureChild(parent: ViewCommon, child: ViewCommon, widthMeasureSpec: number, heightMeasureSpec: number): { measuredWidth: number; measuredHeight: number } {
@ -579,17 +579,27 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
let measureHeight = 0; let measureHeight = 0;
if (child && !child.isCollapsed) { if (child && !child.isCollapsed) {
child._updateEffectiveLayoutValues(parent);
let style = child.style; const widthSpec = parent ? parent._currentWidthMeasureSpec : widthMeasureSpec;
let horizontalMargins = child.effectiveMarginLeft + child.effectiveMarginRight; const heightSpec = parent ? parent._currentHeightMeasureSpec : heightMeasureSpec;
let verticalMargins = child.effectiveMarginTop + child.effectiveMarginBottom;
let childWidthMeasureSpec = ViewCommon.getMeasureSpec(widthMeasureSpec, horizontalMargins, child.effectiveWidth, style.horizontalAlignment === "stretch"); const width = layout.getMeasureSpecSize(widthSpec);
let childHeightMeasureSpec = ViewCommon.getMeasureSpec(heightMeasureSpec, verticalMargins, child.effectiveHeight, style.verticalAlignment === "stretch"); const widthMode = layout.getMeasureSpecMode(widthSpec);
const height = layout.getMeasureSpecSize(heightSpec);
const heightMode = layout.getMeasureSpecMode(heightSpec);
child._updateEffectiveLayoutValues(width, widthMode, height, heightMode);
const style = child.style;
const horizontalMargins = child.effectiveMarginLeft + child.effectiveMarginRight;
const verticalMargins = child.effectiveMarginTop + child.effectiveMarginBottom;
const childWidthMeasureSpec = ViewCommon.getMeasureSpec(widthMeasureSpec, horizontalMargins, child.effectiveWidth, style.horizontalAlignment === "stretch");
const childHeightMeasureSpec = ViewCommon.getMeasureSpec(heightMeasureSpec, verticalMargins, child.effectiveHeight, style.verticalAlignment === "stretch");
if (traceEnabled()) { if (traceEnabled()) {
traceWrite(child.parent + " :measureChild: " + child + " " + layout.measureSpecToString(childWidthMeasureSpec) + ", " + layout.measureSpecToString(childHeightMeasureSpec), traceCategories.Layout); traceWrite(`${child.parent} :measureChild: ${child} ${layout.measureSpecToString(childWidthMeasureSpec)}, ${layout.measureSpecToString(childHeightMeasureSpec)}}`, traceCategories.Layout);
} }
child.measure(childWidthMeasureSpec, childHeightMeasureSpec); child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
@ -760,26 +770,24 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
throw new Error("The View._setValue is obsolete. There is a new property system."); throw new Error("The View._setValue is obsolete. There is a new property system.");
} }
_updateEffectiveLayoutValues(parent: ViewDefinition): void { _updateEffectiveLayoutValues(
parentWidthMeasureSize: number,
parentWidthMeasureMode: number,
parentHeightMeasureSize: number,
parentHeightMeasureMode: number): void {
const style = this.style; const style = this.style;
let parentWidthMeasureSpec = parent._currentWidthMeasureSpec; const availableWidth = parentWidthMeasureMode === layout.UNSPECIFIED ? -1 : parentWidthMeasureSize;
let parentWidthMeasureSize = layout.getMeasureSpecSize(parentWidthMeasureSpec);
let parentWidthMeasureMode = layout.getMeasureSpecMode(parentWidthMeasureSpec);
let parentAvailableWidth = parentWidthMeasureMode === layout.UNSPECIFIED ? -1 : parentWidthMeasureSize;
this.effectiveWidth = PercentLength.toDevicePixels(style.width, -2, parentAvailableWidth); this.effectiveWidth = PercentLength.toDevicePixels(style.width, -2, availableWidth);
this.effectiveMarginLeft = PercentLength.toDevicePixels(style.marginLeft, 0, parentAvailableWidth); this.effectiveMarginLeft = PercentLength.toDevicePixels(style.marginLeft, 0, availableWidth);
this.effectiveMarginRight = PercentLength.toDevicePixels(style.marginRight, 0, parentAvailableWidth); this.effectiveMarginRight = PercentLength.toDevicePixels(style.marginRight, 0, availableWidth);
let parentHeightMeasureSpec = parent._currentHeightMeasureSpec; const availableHeight = parentHeightMeasureMode === layout.UNSPECIFIED ? -1 : parentHeightMeasureSize;
let parentHeightMeasureSize = layout.getMeasureSpecSize(parentHeightMeasureSpec);
let parentHeightMeasureMode = layout.getMeasureSpecMode(parentHeightMeasureSpec);
let parentAvailableHeight = parentHeightMeasureMode === layout.UNSPECIFIED ? -1 : parentHeightMeasureSize;
this.effectiveHeight = PercentLength.toDevicePixels(style.height, -2, parentAvailableHeight); this.effectiveHeight = PercentLength.toDevicePixels(style.height, -2, availableHeight);
this.effectiveMarginTop = PercentLength.toDevicePixels(style.marginTop, 0, parentAvailableHeight); this.effectiveMarginTop = PercentLength.toDevicePixels(style.marginTop, 0, availableHeight);
this.effectiveMarginBottom = PercentLength.toDevicePixels(style.marginBottom, 0, parentAvailableHeight); this.effectiveMarginBottom = PercentLength.toDevicePixels(style.marginBottom, 0, availableHeight);
} }
public _setNativeClipToBounds() { public _setNativeClipToBounds() {

View File

@ -524,7 +524,11 @@ export abstract class View extends ViewBase {
/** /**
* @private * @private
*/ */
_updateEffectiveLayoutValues(parent: View): void; _updateEffectiveLayoutValues(
parentWidthMeasureSize: number,
parentWidthMeasureMode: number,
parentHeightMeasureSize: number,
parentHeightMeasureMode: number): void
/** /**
* @private * @private
*/ */
@ -641,6 +645,7 @@ export const isEnabledProperty: Property<View, boolean>;
export const isUserInteractionEnabledProperty: Property<View, boolean>; export const isUserInteractionEnabledProperty: Property<View, boolean>;
export namespace ios { export namespace ios {
export function updateConstraints(controller: UIViewController, owner: View): void;
export function layoutView(controller: UIViewController, owner: View): void; export function layoutView(controller: UIViewController, owner: View): void;
export class UILayoutViewController extends UIViewController { export class UILayoutViewController extends UIViewController {
public static initWithOwner(owner: WeakRef<View>): UILayoutViewController; public static initWithOwner(owner: WeakRef<View>): UILayoutViewController;

View File

@ -1,5 +1,6 @@
// Definitions. // Definitions.
import { Point, View as ViewDefinition, dip } from "."; import { Point, View as ViewDefinition, dip } from ".";
import { ViewBase } from "../view-base";
import { import {
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty, ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
@ -39,10 +40,6 @@ export class View extends ViewCommon {
*/ */
_nativeBackgroundState: "unset" | "invalid" | "drawn"; _nativeBackgroundState: "unset" | "invalid" | "drawn";
// get nativeView(): UIView {
// return this.ios;
// }
public _addViewCore(view: ViewCommon, atIndex?: number) { public _addViewCore(view: ViewCommon, atIndex?: number) {
super._addViewCore(view, atIndex); super._addViewCore(view, atIndex);
this.requestLayout(); this.requestLayout();
@ -67,13 +64,11 @@ export class View extends ViewCommon {
const parent = <View>this.parent; const parent = <View>this.parent;
if (parent) { if (parent) {
if (!parent.isLayoutRequested) {
parent.requestLayout(); parent.requestLayout();
} }
}
const nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;
if (nativeView && this.isLoaded) { if (nativeView) {
nativeView.setNeedsLayout(); nativeView.setNeedsLayout();
} }
} }
@ -82,7 +77,6 @@ export class View extends ViewCommon {
let measureSpecsChanged = this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec); let measureSpecsChanged = this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
let forceLayout = (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT; let forceLayout = (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT;
if (forceLayout || measureSpecsChanged) { if (forceLayout || measureSpecsChanged) {
// first clears the measured dimension flag // first clears the measured dimension flag
this._privateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; this._privateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
@ -100,7 +94,7 @@ export class View extends ViewCommon {
@profile @profile
public layout(left: number, top: number, right: number, bottom: number, setFrame = true): void { public layout(left: number, top: number, right: number, bottom: number, setFrame = true): void {
let { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom); const { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
if (setFrame) { if (setFrame) {
this.layoutNativeView(left, top, right, bottom); this.layoutNativeView(left, top, right, bottom);
} }
@ -110,14 +104,17 @@ export class View extends ViewCommon {
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED; this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
} }
this.updateBackground(sizeChanged);
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
}
private updateBackground(sizeChanged: boolean): void {
if (sizeChanged) { if (sizeChanged) {
this._onSizeChanged(); this._onSizeChanged();
} else if (this._nativeBackgroundState === "invalid") { } else if (this._nativeBackgroundState === "invalid") {
let background = this.style.backgroundInternal; const background = this.style.backgroundInternal;
this._redrawNativeBackground(background); this._redrawNativeBackground(background);
} }
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
} }
public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void { public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void {
@ -163,7 +160,7 @@ export class View extends ViewCommon {
this._cachedFrame = frame; this._cachedFrame = frame;
if (this._hasTransfrom) { if (this._hasTransfrom) {
// Always set identity transform before setting frame; // Always set identity transform before setting frame;
let transform = nativeView.transform; const transform = nativeView.transform;
nativeView.transform = CGAffineTransformIdentity; nativeView.transform = CGAffineTransformIdentity;
nativeView.frame = frame; nativeView.frame = frame;
nativeView.transform = transform; nativeView.transform = transform;
@ -171,7 +168,8 @@ export class View extends ViewCommon {
else { else {
nativeView.frame = frame; nativeView.frame = frame;
} }
let boundsOrigin = nativeView.bounds.origin;
const boundsOrigin = nativeView.bounds.origin;
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frame.size.width, frame.size.height); nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frame.size.width, frame.size.height);
} }
} }
@ -186,6 +184,22 @@ export class View extends ViewCommon {
this._setNativeViewFrame(nativeView, frame); this._setNativeViewFrame(nativeView, frame);
} }
public _setLayoutFlags(left: number, top: number, right: number, bottom: number): void {
const width = right - left;
const height = bottom - top;
const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY);
const heightSpec = layout.makeMeasureSpec(height, layout.EXACTLY);
this._setCurrentMeasureSpecs(widthSpec, heightSpec);
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
this.setMeasuredDimension(width, height);
const { sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
this.updateBackground(sizeChanged);
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
// NOTE: if there is transformation this frame will be incorrect.
this._cachedFrame = this.nativeViewProtected.frame;
}
public focus(): boolean { public focus(): boolean {
if (this.ios) { if (this.ios) {
return this.ios.becomeFirstResponder(); return this.ios.becomeFirstResponder();
@ -199,7 +213,7 @@ export class View extends ViewCommon {
return undefined; return undefined;
} }
let pointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null); const pointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null);
return { return {
x: pointInWindow.x, x: pointInWindow.x,
y: pointInWindow.y y: pointInWindow.y
@ -211,8 +225,8 @@ export class View extends ViewCommon {
return undefined; return undefined;
} }
let pointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null); const pointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null);
let pointOnScreen = this.nativeViewProtected.window.convertPointToWindow(pointInWindow, null); const pointOnScreen = this.nativeViewProtected.window.convertPointToWindow(pointInWindow, null);
return { return {
x: pointOnScreen.x, x: pointOnScreen.x,
y: pointOnScreen.y y: pointOnScreen.y
@ -226,8 +240,8 @@ export class View extends ViewCommon {
return undefined; return undefined;
} }
let myPointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null); const myPointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null);
let otherPointInWindow = otherView.nativeViewProtected.convertPointToView(otherView.nativeViewProtected.bounds.origin, null); const otherPointInWindow = otherView.nativeViewProtected.convertPointToView(otherView.nativeViewProtected.bounds.origin, null);
return { return {
x: myPointInWindow.x - otherPointInWindow.x, x: myPointInWindow.x - otherPointInWindow.x,
y: myPointInWindow.y - otherPointInWindow.y y: myPointInWindow.y - otherPointInWindow.y
@ -256,15 +270,15 @@ export class View extends ViewCommon {
} }
public updateNativeTransform() { public updateNativeTransform() {
let scaleX = this.scaleX || 1e-6; const scaleX = this.scaleX || 1e-6;
let scaleY = this.scaleY || 1e-6; const scaleY = this.scaleY || 1e-6;
let rotate = this.rotate || 0; const rotate = this.rotate || 0;
let newTransform = CGAffineTransformIdentity; let newTransform = CGAffineTransformIdentity;
newTransform = CGAffineTransformTranslate(newTransform, this.translateX, this.translateY); newTransform = CGAffineTransformTranslate(newTransform, this.translateX, this.translateY);
newTransform = CGAffineTransformRotate(newTransform, rotate * Math.PI / 180); newTransform = CGAffineTransformRotate(newTransform, rotate * Math.PI / 180);
newTransform = CGAffineTransformScale(newTransform, scaleX, scaleY); newTransform = CGAffineTransformScale(newTransform, scaleX, scaleY);
if (!CGAffineTransformEqualToTransform(this.nativeViewProtected.transform, newTransform)) { if (!CGAffineTransformEqualToTransform(this.nativeViewProtected.transform, newTransform)) {
let updateSuspended = this._isPresentationLayerUpdateSuspeneded(); const updateSuspended = this._isPresentationLayerUpdateSuspeneded();
if (!updateSuspended) { if (!updateSuspended) {
CATransaction.begin(); CATransaction.begin();
} }
@ -277,7 +291,7 @@ export class View extends ViewCommon {
} }
public updateOriginPoint(originX: number, originY: number) { public updateOriginPoint(originX: number, originY: number) {
let newPoint = CGPointMake(originX, originY); const newPoint = CGPointMake(originX, originY);
this.nativeViewProtected.layer.anchorPoint = newPoint; this.nativeViewProtected.layer.anchorPoint = newPoint;
if (this._cachedFrame) { if (this._cachedFrame) {
this._setNativeViewFrame(this.nativeViewProtected, this._cachedFrame); this._setNativeViewFrame(this.nativeViewProtected, this._cachedFrame);
@ -300,11 +314,11 @@ export class View extends ViewCommon {
} }
[isEnabledProperty.getDefault](): boolean { [isEnabledProperty.getDefault](): boolean {
let nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;
return nativeView instanceof UIControl ? nativeView.enabled : true; return nativeView instanceof UIControl ? nativeView.enabled : true;
} }
[isEnabledProperty.setNative](value: boolean) { [isEnabledProperty.setNative](value: boolean) {
let nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;
if (nativeView instanceof UIControl) { if (nativeView instanceof UIControl) {
nativeView.enabled = value; nativeView.enabled = value;
} }
@ -446,7 +460,7 @@ export class View extends ViewCommon {
} }
_setNativeClipToBounds() { _setNativeClipToBounds() {
let backgroundInternal = this.style.backgroundInternal; const backgroundInternal = this.style.backgroundInternal;
this.nativeViewProtected.clipsToBounds = this.nativeViewProtected.clipsToBounds =
this.nativeViewProtected instanceof UIScrollView || this.nativeViewProtected instanceof UIScrollView ||
backgroundInternal.hasBorderWidth() || backgroundInternal.hasBorderWidth() ||
@ -461,7 +475,7 @@ export class CustomLayoutView extends View {
constructor() { constructor() {
super(); super();
this.nativeViewProtected = UIView.new(); this.nativeViewProtected = UIView.alloc().initWithFrame(iosUtils.getter(UIScreen, UIScreen.mainScreen).bounds);
} }
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
@ -494,14 +508,28 @@ export class CustomLayoutView extends View {
child.nativeViewProtected.removeFromSuperview(); child.nativeViewProtected.removeFromSuperview();
} }
} }
_getCurrentLayoutBounds(): { left: number; top: number; right: number; bottom: number } {
const nativeView = this.nativeViewProtected;
if (nativeView && !this.isCollapsed) {
const frame = nativeView.frame;
const origin = frame.origin;
const size = frame.size;
return {
left: layout.toDevicePixels(origin.x),
top: layout.toDevicePixels(origin.y),
right: layout.toDevicePixels(origin.x + size.width),
bottom: layout.toDevicePixels(origin.y + size.height)
};
} else {
return { left: 0, top: 0, right: 0, bottom: 0 };
}
}
} }
function isScrollable(controller: UIViewController, owner: View): boolean { function isScrollable(controller: UIViewController, owner: View): boolean {
let scrollable = (<any>owner).scrollableContent; let scrollable = (<any>owner).scrollableContent;
if (scrollable === undefined) { if (scrollable === undefined) {
if (controller.childViewControllers.count > 0) {
scrollable = true;
} else {
let view = controller.view; let view = controller.view;
while (view) { while (view) {
if (view instanceof UIScrollView) { if (view instanceof UIScrollView) {
@ -512,7 +540,6 @@ function isScrollable(controller: UIViewController, owner: View): boolean {
view = view.subviews.count > 0 ? view.subviews[0] : null; view = view.subviews.count > 0 ? view.subviews[0] : null;
} }
} }
}
return scrollable === true || scrollable === "true";; return scrollable === true || scrollable === "true";;
} }
@ -530,50 +557,138 @@ function getStatusBarHeight(controller: UIViewController): number {
return shouldReturnStatusBarHeight ? uiUtils.ios.getStatusBarHeight(controller) : 0; return shouldReturnStatusBarHeight ? uiUtils.ios.getStatusBarHeight(controller) : 0;
} }
const majorVersion = iosUtils.MajorVersion;
interface ExtendedController extends UIViewController {
scrollable: boolean;
navBarHidden: boolean;
hasChildControllers: boolean;
safeAreaLeft: NSLayoutConstraint;
safeAreaTop: NSLayoutConstraint;
safeAreaRight: NSLayoutConstraint;
safeAreaBottom: NSLayoutConstraint;
fullscreenTop: NSLayoutConstraint;
fullscreenBottom: NSLayoutConstraint;
activeConstraints: NSLayoutConstraint[];
}
export namespace ios { export namespace ios {
export function layoutView(controller: UIViewController, owner: View): void { function constrainView(controller: UIViewController, owner: View): void {
const scrollableContent = isScrollable(controller, owner); const root = controller.view;
const view = root.subviews[0];
const extendedController = <ExtendedController>controller;
if (!extendedController.safeAreaTop) {
view.translatesAutoresizingMaskIntoConstraints = false;
if (majorVersion > 10) {
const safeArea = root.safeAreaLayoutGuide;
extendedController.safeAreaTop = view.topAnchor.constraintEqualToAnchor(safeArea.topAnchor);
extendedController.fullscreenTop = view.topAnchor.constraintEqualToAnchor(root.topAnchor);
extendedController.safeAreaBottom = view.bottomAnchor.constraintEqualToAnchor(safeArea.bottomAnchor);
extendedController.fullscreenBottom = view.bottomAnchor.constraintEqualToAnchor(root.bottomAnchor);
extendedController.safeAreaLeft = view.leftAnchor.constraintEqualToAnchor(safeArea.leftAnchor);
extendedController.safeAreaRight = view.rightAnchor.constraintEqualToAnchor(safeArea.rightAnchor);
} else {
extendedController.safeAreaTop = view.topAnchor.constraintEqualToAnchor(controller.topLayoutGuide.bottomAnchor);
extendedController.fullscreenTop = view.topAnchor.constraintEqualToAnchor(root.topAnchor);
extendedController.safeAreaBottom = view.bottomAnchor.constraintEqualToAnchor(controller.bottomLayoutGuide.topAnchor);
extendedController.fullscreenBottom = view.bottomAnchor.constraintEqualToAnchor(root.bottomAnchor);
extendedController.safeAreaLeft = view.leadingAnchor.constraintEqualToAnchor(root.leadingAnchor);
extendedController.safeAreaRight = view.trailingAnchor.constraintEqualToAnchor(root.trailingAnchor);
}
}
const navController = controller.navigationController; const navController = controller.navigationController;
const navBarVisible = navController && !navController.navigationBarHidden; const navBarHidden = navController ? navController.navigationBarHidden : true;
const navBarTranslucent = navController ? navController.navigationBar.translucent : false; const scrollable = (owner && isScrollable(controller, owner));
const hasChildControllers = controller.childViewControllers.count > 0;
const constraints = [
hasChildControllers || scrollable ? extendedController.fullscreenBottom : extendedController.safeAreaBottom,
extendedController.safeAreaLeft,
extendedController.safeAreaRight
];
let navBarHeight = navBarVisible ? uiUtils.ios.getActualHeight(navController.navigationBar) : 0; if (hasChildControllers) {
let statusBarHeight = getStatusBarHeight(controller); // If not inner most extend to fullscreen
constraints.push(extendedController.fullscreenTop);
const edgesForExtendedLayout = controller.edgesForExtendedLayout; } else if (!scrollable) {
const extendedLayoutIncludesOpaqueBars = controller.extendedLayoutIncludesOpaqueBars; // If not scrollable dock under safe area
const layoutExtendsOnTop = (edgesForExtendedLayout & UIRectEdge.Top) === UIRectEdge.Top; constraints.push(extendedController.safeAreaTop);
if (!layoutExtendsOnTop } else if (navBarHidden) {
|| (!extendedLayoutIncludesOpaqueBars && !navBarTranslucent && navBarVisible) // If scrollable but no navigation bar dock under safe area
|| (scrollableContent && navBarVisible)) { constraints.push(extendedController.safeAreaTop);
navBarHeight = 0; } else {
statusBarHeight = 0; // If scrollable and navigation bar extend to fullscreen
constraints.push(extendedController.fullscreenTop);
} }
const tabBarController = controller.tabBarController; const activeConstraints = extendedController.activeConstraints;
const layoutExtendsOnBottom = (edgesForExtendedLayout & UIRectEdge.Bottom) === UIRectEdge.Bottom; if (activeConstraints) {
NSLayoutConstraint.deactivateConstraints(<any>activeConstraints);
}
NSLayoutConstraint.activateConstraints(<any>constraints);
let tabBarHeight = 0; extendedController.scrollable = scrollable;
const tabBarVisible = tabBarController && !tabBarController.tabBar.hidden; extendedController.navBarHidden = navBarHidden;
const tabBarTranslucent = tabBarController ? tabBarController.tabBar.translucent : false; extendedController.hasChildControllers = hasChildControllers;
extendedController.activeConstraints = constraints;
// If tabBar is visible and we don't have scrollableContent and layout
// goes under tabBar we need to reduce available height with tabBar height
if (tabBarVisible && !scrollableContent && layoutExtendsOnBottom && (tabBarTranslucent || extendedLayoutIncludesOpaqueBars)) {
tabBarHeight = tabBarController.tabBar.frame.size.height;
} }
const size = controller.view.bounds.size; export function updateConstraints(controller: UIViewController, owner: View): void {
const extendedController = <ExtendedController>controller;
const navController = controller.navigationController;
const navBarHidden = navController ? navController.navigationBarHidden : true;
const scrollable = (owner && isScrollable(controller, owner));
const hasChildControllers = controller.childViewControllers.count > 0;
if (extendedController.scrollable !== scrollable
|| extendedController.navBarHidden !== navBarHidden
|| extendedController.hasChildControllers !== hasChildControllers) {
constrainView(extendedController, owner);
}
}
export function layoutView(controller: UIViewController, owner: View): void {
// If we are not last controller - don't
if (controller.childViewControllers.count > 0) {// || controller.view !== owner.nativeView.superview) {
return;
}
const frame = controller.beingPresented ? owner.nativeView.superview.frame : controller.view.subviews[0].bounds;
const origin = frame.origin;
const size = frame.size;
const width = layout.toDevicePixels(size.width); const width = layout.toDevicePixels(size.width);
const height = layout.toDevicePixels(size.height - tabBarHeight); const height = layout.toDevicePixels(size.height);
const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY); const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY);
const heightSpec = layout.makeMeasureSpec(height - statusBarHeight - navBarHeight, layout.EXACTLY); const heightSpec = layout.makeMeasureSpec(height, layout.EXACTLY);
owner.measure(widthSpec, heightSpec); View.measureChild(null, owner, widthSpec, heightSpec);
const left = layout.toDevicePixels(origin.x);
const top = layout.toDevicePixels(origin.y);
View.layoutChild(null, owner, left, top, width + left, height + top, false);
// Page.nativeView.frame is never set by our layout... layoutParent(owner.parent);
owner.layout(0, statusBarHeight + navBarHeight, width, height, false); }
function layoutParent(view: ViewBase): void {
if (!view) {
return;
}
if (view instanceof View) {
const frame = view.nativeViewProtected.frame;
const origin = frame.origin;
const size = frame.size;
const left = layout.toDevicePixels(origin.x);
const top = layout.toDevicePixels(origin.y);
const width = layout.toDevicePixels(size.width);
const height = layout.toDevicePixels(size.height);
view._setLayoutFlags(left, top, width + left, height + top);
}
layoutParent(view.parent);
} }
export class UILayoutViewController extends UIViewController { export class UILayoutViewController extends UIViewController {
@ -585,11 +700,14 @@ export namespace ios {
return controller; return controller;
} }
public viewWillLayoutSubviews(): void {
super.viewWillLayoutSubviews();
updateConstraints(this, this.owner.get())
}
public viewDidLayoutSubviews(): void { public viewDidLayoutSubviews(): void {
super.viewDidLayoutSubviews(); super.viewDidLayoutSubviews();
layoutView(this, this.owner.get());
const owner = this.owner.get();
layoutView(this, owner);
} }
} }
} }

View File

@ -228,6 +228,19 @@ export class Frame extends FrameBase {
FrameBase.defaultTransition = value; FrameBase.defaultTransition = value;
} }
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
const widthAndState = View.resolveSizeAndState(width, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(height, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
public layoutNativeView(left: number, top: number, right: number, bottom: number): void { public layoutNativeView(left: number, top: number, right: number, bottom: number): void {
// //
} }

View File

@ -205,8 +205,11 @@ export class FlexboxLayout extends FlexboxLayoutBase {
} }
private _measureHorizontal(widthMeasureSpec: number, heightMeasureSpec: number): void { private _measureHorizontal(widthMeasureSpec: number, heightMeasureSpec: number): void {
const widthMode = getMeasureSpecMode(widthMeasureSpec);
const widthSize = getMeasureSpecSize(widthMeasureSpec); const widthSize = getMeasureSpecSize(widthMeasureSpec);
const widthMode = getMeasureSpecMode(widthMeasureSpec);
const heightSize = getMeasureSpecSize(heightMeasureSpec);
const heightMode = getMeasureSpecMode(heightMeasureSpec);
let childState = 0; let childState = 0;
this._flexLines.length = 0; this._flexLines.length = 0;
@ -231,7 +234,7 @@ export class FlexboxLayout extends FlexboxLayoutBase {
continue; continue;
} }
child._updateEffectiveLayoutValues(this); child._updateEffectiveLayoutValues(widthSize, widthMode, heightSize, heightMode);
let lp = child; // child.style; let lp = child; // child.style;
if (FlexboxLayout.getAlignSelf(child) === "stretch") { if (FlexboxLayout.getAlignSelf(child) === "stretch") {
flexLine._indicesAlignSelfStretch.push(i); flexLine._indicesAlignSelfStretch.push(i);
@ -321,8 +324,10 @@ export class FlexboxLayout extends FlexboxLayoutBase {
} }
private _measureVertical(widthMeasureSpec, heightMeasureSpec): void { private _measureVertical(widthMeasureSpec, heightMeasureSpec): void {
let heightMode = getMeasureSpecMode(heightMeasureSpec); const widthSize = getMeasureSpecSize(widthMeasureSpec);
let heightSize = getMeasureSpecSize(heightMeasureSpec); const widthMode = getMeasureSpecMode(widthMeasureSpec);
const heightSize = getMeasureSpecSize(heightMeasureSpec);
const heightMode = getMeasureSpecMode(heightMeasureSpec);
let childState = 0; let childState = 0;
this._flexLines.length = 0; this._flexLines.length = 0;
@ -346,7 +351,7 @@ export class FlexboxLayout extends FlexboxLayoutBase {
continue; continue;
} }
child._updateEffectiveLayoutValues(this); child._updateEffectiveLayoutValues(widthSize, widthMode, heightSize, heightMode);
const lp = child; // .style; const lp = child; // .style;
if (FlexboxLayout.getAlignSelf(child) === "stretch") { if (FlexboxLayout.getAlignSelf(child) === "stretch") {
flexLine._indicesAlignSelfStretch.push(i); flexLine._indicesAlignSelfStretch.push(i);

View File

@ -3,17 +3,6 @@ import { LayoutBase } from "./layout-base";
export * from "./layout-base"; export * from "./layout-base";
export class Layout extends LayoutBase implements LayoutDefinition { export class Layout extends LayoutBase implements LayoutDefinition {
nativeViewProtected: UIView;
constructor() {
super();
this.nativeViewProtected = UIView.new();
}
get ios(): UIView {
return this.nativeViewProtected;
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
// Don't call super because it will measure the native element. // Don't call super because it will measure the native element.
} }

View File

@ -75,7 +75,6 @@ class UIViewControllerImpl extends UIViewController {
public viewWillAppear(animated: boolean): void { public viewWillAppear(animated: boolean): void {
super.viewWillAppear(animated); super.viewWillAppear(animated);
const owner = this._owner.get(); const owner = this._owner.get();
if (!owner) { if (!owner) {
return; return;
@ -254,6 +253,13 @@ class UIViewControllerImpl extends UIViewController {
} }
} }
public viewWillLayoutSubviews(): void {
super.viewWillLayoutSubviews();
const owner = this._owner.get();
iosView.updateConstraints(this, owner);
}
public viewDidLayoutSubviews(): void { public viewDidLayoutSubviews(): void {
super.viewDidLayoutSubviews(); super.viewDidLayoutSubviews();
@ -264,8 +270,8 @@ class UIViewControllerImpl extends UIViewController {
const whiteColor = new Color("white").ios; const whiteColor = new Color("white").ios;
export class Page extends PageBase { export class Page extends PageBase {
public viewController: UIViewControllerImpl;
nativeViewProtected: UIView; nativeViewProtected: UIView;
viewController: UIViewControllerImpl;
private _ios: UIViewControllerImpl; private _ios: UIViewControllerImpl;
public _enableLoadedEvents: boolean; public _enableLoadedEvents: boolean;
@ -277,6 +283,8 @@ export class Page extends PageBase {
constructor() { constructor() {
super(); super();
const controller = UIViewControllerImpl.initWithOwner(new WeakRef(this)); const controller = UIViewControllerImpl.initWithOwner(new WeakRef(this));
const view = UIView.alloc().initWithFrame(getter(UIScreen, UIScreen.mainScreen).bounds);
controller.view.addSubview(view);
this.viewController = this._ios = controller; this.viewController = this._ios = controller;
this.nativeViewProtected = controller.view; this.nativeViewProtected = controller.view;
this.nativeViewProtected.backgroundColor = whiteColor; this.nativeViewProtected.backgroundColor = whiteColor;
@ -298,12 +306,6 @@ export class Page extends PageBase {
// //
} }
public _onContentChanged(oldView: View, newView: View) {
super._onContentChanged(oldView, newView);
this._removeNativeView(oldView);
this._addNativeView(newView);
}
@profile @profile
public onLoaded() { public onLoaded() {
// loaded/unloaded events are handled in page viewWillAppear/viewDidDisappear // loaded/unloaded events are handled in page viewWillAppear/viewDidDisappear
@ -321,40 +323,6 @@ export class Page extends PageBase {
} }
} }
private _addNativeView(view: View) {
if (view) {
if (traceEnabled()) {
traceWrite("Native: Adding " + view + " to " + this, traceCategories.ViewHierarchy);
}
if (view.ios instanceof UIView) {
this._ios.view.addSubview(view.ios);
} else {
const viewController = view.ios instanceof UIViewController ? view.ios : view.viewController;
if (viewController) {
this._ios.addChildViewController(view.ios);
this._ios.view.addSubview(view.ios.view);
}
}
}
}
private _removeNativeView(view: View) {
if (view) {
if (traceEnabled()) {
traceWrite("Native: Removing " + view + " from " + this, traceCategories.ViewHierarchy);
}
if (view.ios instanceof UIView) {
view.ios.removeFromSuperview();
} else {
const viewController = view.ios instanceof UIViewController ? view.ios : view.viewController;
if (viewController) {
view.ios.removeFromParentViewController();
view.ios.view.removeFromSuperview();
}
}
}
}
protected _showNativeModalView(parent: Page, context: any, closeCallback: Function, fullscreen?: boolean) { protected _showNativeModalView(parent: Page, context: any, closeCallback: Function, fullscreen?: boolean) {
super._showNativeModalView(parent, context, closeCallback, fullscreen); super._showNativeModalView(parent, context, closeCallback, fullscreen);
this._modalParent = parent; this._modalParent = parent;
@ -421,23 +389,6 @@ export class Page extends PageBase {
} }
} }
public _updateEffectiveLayoutValues(parent: View): void {
super._updateEffectiveLayoutValues(parent);
// Patch vertical margins to respect status bar height
if (!this.backgroundSpanUnderStatusBar) {
const style = this.style;
const parentHeightMeasureSpec = parent._currentHeightMeasureSpec;
const parentHeightMeasureSize = layout.getMeasureSpecSize(parentHeightMeasureSpec) - uiUtils.ios.getStatusBarHeight();
const parentHeightMeasureMode = layout.getMeasureSpecMode(parentHeightMeasureSpec);
const parentAvailableHeight = parentHeightMeasureMode === layout.UNSPECIFIED ? -1 : parentHeightMeasureSize;
this.effectiveMarginTop = PercentLength.toDevicePixels(style.marginTop, 0, parentAvailableHeight);
this.effectiveMarginBottom = PercentLength.toDevicePixels(style.marginBottom, 0, parentAvailableHeight);
}
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) { public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) {
const width = layout.getMeasureSpecSize(widthMeasureSpec); const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec); const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
@ -450,9 +401,7 @@ export class Page extends PageBase {
View.measureChild(this, this.actionBar, widthMeasureSpec, layout.makeMeasureSpec(height, layout.AT_MOST)); View.measureChild(this, this.actionBar, widthMeasureSpec, layout.makeMeasureSpec(height, layout.AT_MOST));
} }
const heightSpec = layout.makeMeasureSpec(height, heightMode); const result = View.measureChild(this, this.layoutView, widthMeasureSpec, heightMeasureSpec);
const result = View.measureChild(this, this.layoutView, widthMeasureSpec, heightSpec);
const measureWidth = Math.max(result.measuredWidth, this.effectiveMinWidth); const measureWidth = Math.max(result.measuredWidth, this.effectiveMinWidth);
const measureHeight = Math.max(result.measuredHeight, this.effectiveMinHeight); const measureHeight = Math.max(result.measuredHeight, this.effectiveMinHeight);
@ -468,31 +417,59 @@ export class Page extends PageBase {
View.layoutChild(this, this.layoutView, 0, top, right - left, bottom); View.layoutChild(this, this.layoutView, 0, top, right - left, bottom);
} }
public _addViewToNativeVisualTree(view: View): boolean { public _addViewToNativeVisualTree(child: View, atIndex: number): boolean {
// ActionBar is handled by the UINavigationController // ActionBar is handled by the UINavigationController
if (view === this.actionBar) { if (child === this.actionBar) {
return true; return true;
} }
return super._addViewToNativeVisualTree(view); // Don't add modal pages our visual tree.
if (child !== this.content) {
return true;
} }
public _removeViewFromNativeVisualTree(view: View): void { const nativeParent = this.nativeViewProtected.subviews[0];
const nativeChild = child.nativeViewProtected;
const viewController = child.ios instanceof UIViewController ? child.ios : child.viewController;
if (viewController) {
this.viewController.addChildViewController(viewController);
}
if (nativeParent && nativeChild) {
if (typeof atIndex !== "number" || atIndex >= nativeParent.subviews.count) {
nativeParent.addSubview(nativeChild);
} else {
nativeParent.insertSubviewAtIndex(nativeChild, atIndex);
}
return true;
}
return false;
}
public _removeViewFromNativeVisualTree(child: View): void {
// ActionBar is handled by the UINavigationController // ActionBar is handled by the UINavigationController
if (view === this.actionBar) { if (child === this.actionBar) {
return; return;
} }
super._removeViewFromNativeVisualTree(view); const viewController = child.ios instanceof UIViewController ? child.ios : child.viewController;
if (viewController) {
viewController.removeFromParentViewController();
}
super._removeViewFromNativeVisualTree(child);
} }
[actionBarHiddenProperty.setNative](value: boolean) { [actionBarHiddenProperty.setNative](value: boolean) {
this._updateEnableSwipeBackNavigation(value); this._updateEnableSwipeBackNavigation(value);
if (this.isLoaded) { invalidateTopmostController(this.viewController);
// Update nav-bar visibility with disabled animations // Update nav-bar visibility with disabled animations
this.updateActionBar(true); this.updateActionBar(true);
} }
}
[statusBarStyleProperty.getDefault](): UIBarStyle { [statusBarStyleProperty.getDefault](): UIBarStyle {
return UIBarStyle.Default; return UIBarStyle.Default;
@ -509,3 +486,31 @@ export class Page extends PageBase {
} }
} }
} }
function invalidateTopmostController(controller: UIViewController): void {
if (!controller) {
return;
}
controller.view.setNeedsLayout();
const presentedViewController = controller.presentedViewController;
if (presentedViewController) {
return invalidateTopmostController(presentedViewController);
}
const childControllers = controller.childViewControllers;
let size = controller.childViewControllers.count;
while (size > 0) {
const childController = childControllers[--size];
if (childController instanceof UITabBarController) {
invalidateTopmostController(childController.selectedViewController);
} else if (childController instanceof UINavigationController) {
invalidateTopmostController(childController.topViewController);
} else if (childController instanceof UISplitViewController) {
invalidateTopmostController(childController.viewControllers.lastObject);
} else {
invalidateTopmostController(childController);
}
}
}

View File

@ -1,5 +1,8 @@
import { ScrollEventData } from "."; import { ScrollEventData } from ".";
import { View, layout, ScrollViewBase, scrollBarIndicatorVisibleProperty } from "./scroll-view-common"; import { View, layout, ScrollViewBase, scrollBarIndicatorVisibleProperty } from "./scroll-view-common";
// HACK: Webpack. Use a fully-qualified import to allow resolve.extensions(.ios.js) to
// kick in. `../utils` doesn't seem to trigger the webpack extensions mechanism.
import * as uiUtils from "tns-core-modules/ui/utils";
export * from "./scroll-view-common"; export * from "./scroll-view-common";
@ -143,10 +146,21 @@ export class ScrollView extends ScrollViewBase {
const width = (right - left); const width = (right - left);
const height = (bottom - top); const height = (bottom - top);
if (this.orientation === "horizontal") { let verticalInset: number;
View.layoutChild(this, this.layoutView, 0, 0, Math.max(this._contentMeasuredWidth, width), height); const nativeView = this.nativeViewProtected;
const inset = nativeView.adjustedContentInset;
// Prior iOS 11
if (inset === undefined) {
verticalInset = -layout.toDevicePixels(nativeView.contentOffset.y);
verticalInset += getTabBarHeight(this);
} else { } else {
View.layoutChild(this, this.layoutView, 0, 0, width, Math.max(this._contentMeasuredHeight, height)); verticalInset = layout.toDevicePixels(inset.bottom + inset.top);
}
if (this.orientation === "horizontal") {
View.layoutChild(this, this.layoutView, 0, 0, Math.max(this._contentMeasuredWidth, width), height - verticalInset);
} else {
View.layoutChild(this, this.layoutView, 0, 0, width, Math.max(this._contentMeasuredHeight, height - verticalInset));
} }
} }
@ -155,4 +169,18 @@ export class ScrollView extends ScrollViewBase {
} }
} }
function getTabBarHeight(scrollView: ScrollView): number {
let parent = scrollView.parent;
while (parent) {
const controller = parent.viewController;
if (controller instanceof UITabBarController) {
return uiUtils.ios.getActualHeight(controller.tabBar);
}
parent = parent.parent;
}
return 0;
}
ScrollView.prototype.recycleNativeView = "auto"; ScrollView.prototype.recycleNativeView = "auto";

View File

@ -126,14 +126,24 @@ function updateItemIconPosition(tabBarItem: UITabBarItem): void {
export class TabViewItem extends TabViewItemBase { export class TabViewItem extends TabViewItemBase {
private __controller: UIViewController; private __controller: UIViewController;
public setViewController(controller: UIViewController) { private _setNeedsLayoutOnSuperview: boolean;
public setViewController(controller: UIViewController, nativeView: UIView) {
this.__controller = controller; this.__controller = controller;
this.setNativeView(controller.view); this.setNativeView(nativeView);
this._setNeedsLayoutOnSuperview = controller.view !== nativeView;
}
public requestLayout(): void {
super.requestLayout();
if (this._setNeedsLayoutOnSuperview) {
this.nativeViewProtected.superview.setNeedsLayout();
}
} }
public disposeNativeView() { public disposeNativeView() {
this.__controller = undefined; this.__controller = undefined;
this.setNativeView(undefined); this.setNativeView(undefined);
this._setNeedsLayoutOnSuperview = false;
} }
public _update() { public _update() {
@ -207,6 +217,19 @@ export class TabView extends TabViewBase {
// //
} }
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = 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: UIViewController) { public _onViewControllerShown(viewController: UIViewController) {
// This method could be called with the moreNavigationController or its list controller, so we have to check. // This method could be called with the moreNavigationController or its list controller, so we have to check.
if (traceEnabled()) { if (traceEnabled()) {
@ -262,17 +285,21 @@ export class TabView extends TabViewBase {
let newController: UIViewController = item.view ? item.view.viewController : null; let newController: UIViewController = item.view ? item.view.viewController : null;
if (newController) { if (newController) {
item.setViewController(newController, newController.view);
return newController; return newController;
} }
if (item.view.ios instanceof UIViewController) { if (item.view.ios instanceof UIViewController) {
newController = item.view.ios; newController = item.view.ios;
item.setViewController(newController, newController.view);
} else if (item.view.ios && item.view.ios.controller instanceof UIViewController) { } else if (item.view.ios && item.view.ios.controller instanceof UIViewController) {
newController = item.view.ios.controller; newController = item.view.ios.controller;
item.setViewController(newController, newController.view);
} else { } else {
newController = iosView.UILayoutViewController.initWithOwner(new WeakRef(item.view)); newController = iosView.UILayoutViewController.initWithOwner(new WeakRef(item.view));
newController.view = item.view.nativeViewProtected; newController.view.addSubview(item.view.nativeViewProtected);
item.view.viewController = newController; item.view.viewController = newController;
item.setViewController(newController, item.view.nativeViewProtected);
} }
return newController; return newController;
@ -291,8 +318,6 @@ export class TabView extends TabViewBase {
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const item = items[i]; const item = items[i];
const controller = this.getViewController(item); const controller = this.getViewController(item);
item.setViewController(controller);
const icon = this._getIcon(item.iconSource); const icon = this._getIcon(item.iconSource);
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((item.title || ""), icon, i); const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((item.title || ""), icon, i);
if (!icon) { if (!icon) {

View File

@ -3,8 +3,6 @@
declare function float(num: number): any; declare function float(num: number): any;
declare function long(num: number): any; declare function long(num: number): any;
declare var app;
declare var telerik;
declare var gc: () => void; declare var gc: () => void;
declare function float(num: number): any; declare function float(num: number): any;
@ -13,44 +11,3 @@ declare function long(num: number): any;
interface ArrayConstructor { interface ArrayConstructor {
create(type: any, count: number): any; create(type: any, count: number): any;
} }
declare module android {
module support {
module v4 {
module widget {
class DrawerLayout {
constructor(context: android.content.Context);
}
module DrawerLayout {
class DrawerListener implements IDrawerListener {
constructor(implementation: IDrawerListener);
onDrawerClosed(drawerView: android.view.View): void;
onDrawerOpened(drawerView: android.view.View): void;
onDrawerSlide(drawerView: android.view.View, offset: number): void;
onDrawerStateChanged(newState: number): void;
}
class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
constructor(width: number, height: number, gravity?: number);
gravity: number;
}
interface IDrawerListener {
onDrawerClosed(drawerView: android.view.View): void;
onDrawerOpened(drawerView: android.view.View): void;
onDrawerSlide(drawerView: android.view.View, offset: number): void;
onDrawerStateChanged(newState: number): void;
}
}
}
module app {
class ActionBarDrawerToggle {
constructor(activity: android.app.Activity, layout: widget.DrawerLayout, imageResId: number, openResId: number, closeResId: number);
}
}
}
}
}