mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-26 22:00:17 +08:00
iOS Frame, Page and TabView measure/layout methods removed. We now rely on the framework positioning. This will result in a change that width, height, minWidth, minHeight, margins not respected on these controls
iOS layout positioning now respects native properties like automaticallyAdjustsScrollViewInsets, edgesForExtendedLayout, extendedLayoutIncludesOpaqueBars, navigationBar.translucent, tabBar.translucent Removed frame-tests.ios.ts - those tests are now invalid Added new layout tests inside page-tests.ios.ts Commented few asserts in scroll-view-tests View now expose ios namespace with layoutView method and UILayoutViewController used by page, tab-view and application module ViewBase now expose viewController property that should be set from all widgets that are using viewcontrollers internally (like Page, Frame, TabView) ViewBase now sets ios property to either the view returned from createNativeView or to nativeViewProptected fragment.transitions now use animation/transition start to add fragments to waitingQueue. Before we did it manually in navigate/goBack. This way we can reuse the fragment.transition when calling showDialog. Also when animation/transition ends we check the animation/transition to see if this fragment should be set as current. Frame expose new loadViewFromEntry method (to load a view from URI) Frame navigation happens once frame is loaded Frame now supports Page as a child in XML Fixed GridLayout row, rowSpan, column, columnSpan properties type Fixed bug in GridLayout where add/remove of columns/rows won't update the internal state of the grid (backport from android when GridLayout is recycled) ListView will no longer invalidate layout when cell is removed Fixed bug in ScrollView ios where effectiveMinWidth/Height was multiplied to density (it is already on device pixels so no need to multiply) TabView android now calls loaded only on the selected child (not all) Core refactoring
This commit is contained in:
1
apps/.vscode/launch.json
vendored
1
apps/.vscode/launch.json
vendored
@ -9,6 +9,7 @@
|
||||
"appRoot": "${workspaceRoot}",
|
||||
"sourceMaps": true,
|
||||
"watch": true,
|
||||
"stopOnEntry": true,
|
||||
"tnsArgs": [
|
||||
"--sync-all-files"
|
||||
]
|
||||
|
@ -1,9 +1,4 @@
|
||||
import * as application from "tns-core-modules/application";
|
||||
import * as fps from "tns-core-modules/fps-meter";
|
||||
fps.addCallback(function (fps, minFps) {
|
||||
console.info("fps=" + fps + " minFps=" + minFps);
|
||||
});
|
||||
fps.start();
|
||||
|
||||
// Start the application
|
||||
application.start({ moduleName: "cuteness.io/main-page" });
|
||||
application.start({ moduleName: "cuteness.io/main-page" });
|
@ -12,11 +12,6 @@ export function pageLoaded(args: ObservableEventData) {
|
||||
var page = <Page>args.object;
|
||||
|
||||
page.bindingContext = appViewModel;
|
||||
|
||||
// Enable platform specific feature (in this case Android page caching)
|
||||
if (topmostFrame().android) {
|
||||
topmostFrame().android.cachePagesOnNavigate = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function listViewItemTap(args: ListViewItemEventData) {
|
||||
|
@ -211,7 +211,7 @@ export function assertEqual<T extends { equals?(arg: T): boolean } | any>(actual
|
||||
}
|
||||
}
|
||||
else if (actual !== expected) {
|
||||
throw new Error(`${message} Actual: <${actual}>(${typeof (actual)}). Expected: <${expected}>(${typeof (expected)})`);
|
||||
throw new Error(`${message} Actual: <${actual}>(${typeof (actual)}). Expected: <${expected}>(${typeof (expected)})` );
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,7 +350,7 @@ export function wait(seconds: number): void {
|
||||
waitUntilReady(() => false, seconds, false);
|
||||
}
|
||||
|
||||
export function waitUntilReady(isReady: () => boolean, timeoutSec: number = 300, shouldThrow: boolean = true) {
|
||||
export function waitUntilReady(isReady: () => boolean, timeoutSec: number = 3, shouldThrow: boolean = true) {
|
||||
if (!isReady) {
|
||||
return;
|
||||
}
|
||||
|
@ -86,8 +86,8 @@ application.on(application.lowMemoryEvent, function (args: application.Applicati
|
||||
|
||||
application.on(application.uncaughtErrorEvent, function (args: application.UnhandledErrorEventData) {
|
||||
console.log("NativeScriptError: " + args.error);
|
||||
console.log((<any>args.error).nativeException);
|
||||
console.log((<any>args.error).stackTrace);
|
||||
console.log((<any>args.error).nativeException || (<any>args.error).nativeError);
|
||||
console.log((<any>args.error).stackTrace || (<any>args.error).stack);
|
||||
});
|
||||
|
||||
// Android activity events
|
||||
|
@ -346,7 +346,7 @@ function showReportPage(finalMessage: string) {
|
||||
page.className = unsetValue;
|
||||
page.bindingContext = unsetValue;
|
||||
page.style.color = unsetValue;
|
||||
page.style.backgroundColor = unsetValue;
|
||||
page.backgroundColor = "white";
|
||||
page.content = stack;
|
||||
messageContainer.focus();
|
||||
page.style.fontSize = 11;
|
||||
|
@ -61,4 +61,4 @@ export function test_percent_margin_set_to_page_support() {
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.marginTop, 0));
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.marginRight, 0));
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.marginBottom, 0));
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
import * as frameModule from "tns-core-modules/ui/frame";
|
||||
import * as TKUnit from "../../TKUnit";
|
||||
import * as uiUtils from "tns-core-modules/ui/utils";
|
||||
import { PercentLength, unsetValue } from "tns-core-modules/ui/core/view";
|
||||
|
||||
export function test_percent_width_and_height_set_to_page_support() {
|
||||
let topFrame = frameModule.topmost();
|
||||
let currentPage = topFrame.currentPage;
|
||||
|
||||
(<any>currentPage).width = "50%";
|
||||
(<any>currentPage).height = "50%";
|
||||
|
||||
TKUnit.waitUntilReady(() => {
|
||||
return currentPage.isLayoutValid;
|
||||
}, 1);
|
||||
|
||||
let topFrameWidth = topFrame.getMeasuredWidth();
|
||||
let topFrameHeight = topFrame.getMeasuredHeight();
|
||||
|
||||
let currentPageWidth = currentPage.getMeasuredWidth();
|
||||
let currentPageHeight = currentPage.getMeasuredHeight();
|
||||
|
||||
TKUnit.assertEqual(currentPageWidth, Math.round(topFrameWidth / 2), "Current page measuredWidth incorrect");
|
||||
TKUnit.assertEqual(currentPageHeight, Math.round(topFrameHeight / 2), "Current page measuredHeight incorrect");
|
||||
|
||||
//reset values.
|
||||
currentPage.height = unsetValue;
|
||||
currentPage.width = unsetValue;
|
||||
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.width, "auto"));
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.height, "auto"));
|
||||
}
|
||||
|
||||
export function test_percent_margin_set_to_page_support() {
|
||||
percent_margin_set_to_page_support(false);
|
||||
}
|
||||
|
||||
export function test_percent_margin_set_to_page_support_with_backgroundSpanUnderStatusBar() {
|
||||
percent_margin_set_to_page_support(true);
|
||||
}
|
||||
|
||||
function percent_margin_set_to_page_support(backgroundSpanUnderStatusBar: boolean) {
|
||||
const topFrame = frameModule.topmost();
|
||||
const currentPage = topFrame.currentPage;
|
||||
|
||||
const topFrameWidth = topFrame.getMeasuredWidth();
|
||||
const topFrameHeight = topFrame.getMeasuredHeight();
|
||||
const statusBar = backgroundSpanUnderStatusBar ? 0 : uiUtils.ios.getStatusBarHeight();
|
||||
|
||||
currentPage.margin = "10%";
|
||||
currentPage.backgroundSpanUnderStatusBar = backgroundSpanUnderStatusBar;
|
||||
|
||||
TKUnit.waitUntilReady(() => {
|
||||
return currentPage.isLayoutValid;
|
||||
}, 1);
|
||||
|
||||
const currentPageWidth = currentPage.getMeasuredWidth();
|
||||
const currentPageHeight = currentPage.getMeasuredHeight();
|
||||
const marginWidth = Math.round(topFrameWidth * 0.1);
|
||||
const marginHeight = Math.round((topFrameHeight - statusBar) * 0.1);
|
||||
|
||||
// expected page size
|
||||
TKUnit.assertEqual(currentPageWidth, topFrameWidth - 2 * marginWidth, "Page measure width");
|
||||
TKUnit.assertEqual(currentPageHeight, topFrameHeight - 2 * marginHeight - statusBar, "Page measure height");
|
||||
|
||||
// expected page bounds
|
||||
const bounds = currentPage._getCurrentLayoutBounds();
|
||||
TKUnit.assertEqual(bounds.left, Math.round(marginWidth), "Current page LEFT position incorrect");
|
||||
TKUnit.assertEqual(bounds.top, Math.round(marginHeight + statusBar), "Current page TOP position incorrect");
|
||||
TKUnit.assertEqual(bounds.right, Math.round(marginWidth + currentPageWidth), "Current page RIGHT position incorrect");
|
||||
TKUnit.assertEqual(bounds.bottom, Math.round(marginHeight + statusBar + currentPageHeight), "Current page BOTTOM position incorrect");
|
||||
|
||||
//reset values.
|
||||
currentPage.margin = "0";
|
||||
TKUnit.waitUntilReady(() => {
|
||||
return currentPage.isLayoutValid;
|
||||
}, 1);
|
||||
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.marginLeft, 0));
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.marginTop, 0));
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.marginRight, 0));
|
||||
TKUnit.assertTrue(PercentLength.equals(currentPage.marginBottom, 0));
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import * as PageTestCommon from "./page-tests-common";
|
||||
import {Page} from "tns-core-modules/ui/page";
|
||||
import * as TKUnit from "../../TKUnit";
|
||||
import {Label} from "tns-core-modules/ui/label";
|
||||
import * as helper from "../helper";
|
||||
import {View} from "tns-core-modules/ui/core/view";
|
||||
import {EventData} from "tns-core-modules/data/observable";
|
||||
import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view";
|
||||
import { Page, layout, View, EventData } from "tns-core-modules/ui/page";
|
||||
import { Label } from "tns-core-modules/ui/label";
|
||||
import { topmost } from "tns-core-modules/ui/frame";
|
||||
import * as uiUtils from "tns-core-modules/ui/utils";
|
||||
import * as frame from "tns-core-modules/ui/frame";
|
||||
import * as TKUnit from "../../TKUnit";
|
||||
import * as helper from "../helper";
|
||||
import * as PageTestCommon from "./page-tests-common";
|
||||
|
||||
global.moduleMerge(PageTestCommon, exports);
|
||||
|
||||
@ -68,43 +67,281 @@ export function test_WhenShowingModalPageUnloadedIsNotFiredForTheMasterPage() {
|
||||
masterPage.off(View.unloadedEvent, unloadedEventHandler);
|
||||
}
|
||||
|
||||
export function test_page_no_anctionBar_measure_no_spanUnderBackground_measure_layout_size_isCorrect() {
|
||||
let page = new Page();
|
||||
page.backgroundSpanUnderStatusBar = true;
|
||||
page.actionBarHidden = true;
|
||||
let lbl = new Label();
|
||||
page.content = lbl;
|
||||
function getHeight(view: View): number {
|
||||
const bounds = view._getCurrentLayoutBounds();
|
||||
return bounds.bottom - bounds.top;
|
||||
}
|
||||
|
||||
export function test_correct_layout_scrollable_content_false() {
|
||||
const page = new Page();
|
||||
(<any>page).scrollableContent = false;
|
||||
page.actionBar.title = "ActionBar";
|
||||
page.actionBarHidden = true;
|
||||
|
||||
const tabView = new TabView();
|
||||
const tabItem = new TabViewItem();
|
||||
tabItem.title = "Item";
|
||||
const lbl = new Label();
|
||||
tabItem.view = lbl;
|
||||
tabView.items = [tabItem];
|
||||
|
||||
page.content = tabView;
|
||||
helper.navigate(() => page);
|
||||
TKUnit.waitUntilReady(() => page.isLayoutValid);
|
||||
TKUnit.assertTrue(page.isLoaded, "page NOT loaded!");
|
||||
|
||||
let bounds = page._getCurrentLayoutBounds();
|
||||
let pageHeight = bounds.bottom - bounds.top;
|
||||
let frameBounds = page.frame._getCurrentLayoutBounds();
|
||||
let frameHeight = frameBounds.bottom - frameBounds.top;
|
||||
TKUnit.assertEqual(pageHeight, frameHeight, "Page height should match Frame height.");
|
||||
const statusBarHeight = uiUtils.ios.getStatusBarHeight(page.viewController);
|
||||
const tabBarHeight = uiUtils.ios.getActualHeight(tabView.viewController.tabBar);
|
||||
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
|
||||
|
||||
let contentHeight = lbl._getCurrentLayoutBounds().bottom - lbl._getCurrentLayoutBounds().top;
|
||||
let statusBarHeight = uiUtils.ios.getStatusBarHeight();
|
||||
TKUnit.assertEqual(contentHeight, frameHeight - statusBarHeight, "Page.content height should match Frame height - statusBar height.");
|
||||
let pageHeight = getHeight(page);
|
||||
let expectedPageHeight = screenHeight - statusBarHeight;
|
||||
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar");
|
||||
|
||||
page.backgroundSpanUnderStatusBar = false;
|
||||
TKUnit.waitUntilReady(() => page.isLayoutValid);
|
||||
pageHeight = page._getCurrentLayoutBounds().bottom - page._getCurrentLayoutBounds().top;
|
||||
TKUnit.assertEqual(pageHeight, frameHeight - statusBarHeight, "Page should be given Frame height - statusBar height.");
|
||||
|
||||
contentHeight = lbl._getCurrentLayoutBounds().bottom - lbl._getCurrentLayoutBounds().top;
|
||||
TKUnit.assertEqual(contentHeight, pageHeight, "Page.content height should match Page height.");
|
||||
let contentHeight = getHeight(lbl);
|
||||
let expectedLabelHeight = screenHeight - statusBarHeight - tabBarHeight;
|
||||
TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar - tabBar");
|
||||
|
||||
page.actionBarHidden = false;
|
||||
TKUnit.waitUntilReady(() => page.isLayoutValid);
|
||||
|
||||
pageHeight = page._getCurrentLayoutBounds().bottom - page._getCurrentLayoutBounds().top;
|
||||
TKUnit.assertEqual(pageHeight, frameHeight - statusBarHeight, "Page should be given Frame height - statusBar height.");
|
||||
pageHeight = getHeight(page);
|
||||
const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar);
|
||||
expectedPageHeight = screenHeight - statusBarHeight - navBarHeight;
|
||||
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar - navBarHeight");
|
||||
|
||||
contentHeight = lbl._getCurrentLayoutBounds().bottom - lbl._getCurrentLayoutBounds().top;
|
||||
TKUnit.assertTrue(contentHeight < pageHeight, "Page.content be given less space than Page when ActionBar is shown.");
|
||||
contentHeight = getHeight(lbl);
|
||||
expectedLabelHeight = screenHeight - statusBarHeight - tabBarHeight - navBarHeight;
|
||||
TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBarHeight - tabBarHeight - navBarHeight");
|
||||
}
|
||||
|
||||
export function test_correct_layout_scrollable_content_true() {
|
||||
const page = new Page();
|
||||
(<any>page).scrollableContent = true;
|
||||
page.actionBar.title = "ActionBar";
|
||||
page.actionBarHidden = true;
|
||||
|
||||
const tabView = new TabView();
|
||||
const tabItem = new TabViewItem();
|
||||
tabItem.title = "Item";
|
||||
const lbl = new Label();
|
||||
(<any>lbl).scrollableContent = true;
|
||||
tabItem.view = lbl;
|
||||
tabView.items = [tabItem];
|
||||
|
||||
page.content = tabView;
|
||||
helper.navigate(() => page);
|
||||
TKUnit.assertTrue(page.isLoaded, "page NOT loaded!");
|
||||
|
||||
const statusBarHeight = uiUtils.ios.getStatusBarHeight(page.viewController);
|
||||
const tabBarHeight = uiUtils.ios.getActualHeight(tabView.viewController.tabBar);
|
||||
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
|
||||
|
||||
let pageHeight = getHeight(page);
|
||||
let expectedPageHeight = screenHeight - statusBarHeight;
|
||||
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar");
|
||||
|
||||
let contentHeight = getHeight(lbl);
|
||||
let expectedLabelHeight = screenHeight - statusBarHeight;
|
||||
TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar - tabBar");
|
||||
|
||||
page.actionBarHidden = false;
|
||||
TKUnit.waitUntilReady(() => page.isLayoutValid);
|
||||
|
||||
pageHeight = getHeight(page);
|
||||
const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar);
|
||||
expectedPageHeight = screenHeight;
|
||||
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight");
|
||||
|
||||
contentHeight = getHeight(lbl);
|
||||
TKUnit.assertEqual(contentHeight, screenHeight, "lbl.height !== screenHeight");
|
||||
}
|
||||
|
||||
export function test_correct_layout_scrollable_content_true_flat_action_bar() {
|
||||
const page = new Page();
|
||||
(<any>page).scrollableContent = true;
|
||||
page.actionBar.title = "ActionBar";
|
||||
page.actionBar.flat = true;
|
||||
|
||||
const tabView = new TabView();
|
||||
const tabItem = new TabViewItem();
|
||||
tabItem.title = "Item";
|
||||
const lbl = new Label();
|
||||
tabItem.view = lbl;
|
||||
tabView.items = [tabItem];
|
||||
|
||||
page.content = tabView;
|
||||
helper.navigate(() => page);
|
||||
TKUnit.assertTrue(page.isLoaded, "page NOT loaded!");
|
||||
|
||||
const statusBarHeight = uiUtils.ios.getStatusBarHeight(page.viewController);
|
||||
const tabBarHeight = uiUtils.ios.getActualHeight(tabView.viewController.tabBar);
|
||||
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
|
||||
const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar);
|
||||
|
||||
const pageHeight = getHeight(page);
|
||||
const expectedPageHeight = screenHeight - statusBarHeight - navBarHeight;
|
||||
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar - navBarHeight");
|
||||
|
||||
const contentHeight = getHeight(lbl);
|
||||
const expectedLabelHeight = screenHeight - statusBarHeight - navBarHeight - tabBarHeight;
|
||||
TKUnit.assertEqual(contentHeight, expectedLabelHeight, "lbl.height !== screenHeight - statusBar - navBarHeight - tabBarHeight");
|
||||
}
|
||||
|
||||
|
||||
export function test_correct_layout_scrollable_content_true_flat_action_bar_edges_span_under_opaque_bars() {
|
||||
const page = new Page();
|
||||
(<any>page).scrollableContent = true;
|
||||
page.viewController.extendedLayoutIncludesOpaqueBars = true;
|
||||
page.actionBar.title = "ActionBar";
|
||||
page.actionBar.flat = true;
|
||||
|
||||
const tabView = new TabView();
|
||||
tabView.viewController.tabBar.translucent = false;
|
||||
|
||||
const tabItem = new TabViewItem();
|
||||
tabItem.title = "Item";
|
||||
const lbl = new Label();
|
||||
(<any>lbl).scrollableContent = true;
|
||||
tabItem.view = lbl;
|
||||
tabView.items = [tabItem];
|
||||
|
||||
page.content = tabView;
|
||||
helper.navigate(() => page);
|
||||
TKUnit.assertTrue(page.isLoaded, "page NOT loaded!");
|
||||
|
||||
lbl.viewController.extendedLayoutIncludesOpaqueBars = true;
|
||||
lbl.requestLayout();
|
||||
(<UIView>lbl.nativeViewProtected).setNeedsLayout();
|
||||
(<UIView>lbl.nativeViewProtected).layoutIfNeeded();
|
||||
helper.waitUntilLayoutReady(lbl);
|
||||
|
||||
const statusBarHeight = uiUtils.ios.getStatusBarHeight(page.viewController);
|
||||
const tabBarHeight = uiUtils.ios.getActualHeight(tabView.viewController.tabBar);
|
||||
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
|
||||
const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar);
|
||||
|
||||
const pageHeight = getHeight(page);
|
||||
TKUnit.assertEqual(pageHeight, screenHeight, "page.height !== screenHeight");
|
||||
|
||||
const contentHeight = getHeight(lbl);
|
||||
TKUnit.assertEqual(contentHeight, screenHeight, "lbl.height !== screenHeight");
|
||||
}
|
||||
|
||||
export function test_correct_layout_scrollable_content_true_top_edge_does_not_span() {
|
||||
const page = new Page();
|
||||
(<any>page).scrollableContent = true;
|
||||
page.actionBar.title = "ActionBar";
|
||||
(<UIViewController>page.viewController).edgesForExtendedLayout = UIRectEdge.Bottom | UIRectEdge.Left | UIRectEdge.Right;
|
||||
|
||||
const tabView = new TabView();
|
||||
const tabItem = new TabViewItem();
|
||||
tabItem.title = "Item";
|
||||
const lbl = new Label();
|
||||
(<any>lbl).scrollableContent = true;
|
||||
tabItem.view = lbl;
|
||||
tabView.items = [tabItem];
|
||||
|
||||
page.content = tabView;
|
||||
helper.navigate(() => page);
|
||||
TKUnit.assertTrue(page.isLoaded, "page NOT loaded!");
|
||||
|
||||
const statusBarHeight = uiUtils.ios.getStatusBarHeight(page.viewController);
|
||||
const tabBarHeight = uiUtils.ios.getActualHeight(tabView.viewController.tabBar);
|
||||
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
|
||||
const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar);
|
||||
|
||||
const pageHeight = getHeight(page);
|
||||
const expectedPageHeight = screenHeight - statusBarHeight - navBarHeight;
|
||||
TKUnit.assertEqual(pageHeight, expectedPageHeight, "page.height !== screenHeight - statusBar - navBarHeight");
|
||||
|
||||
const contentHeight = getHeight(lbl);
|
||||
TKUnit.assertEqual(contentHeight, expectedPageHeight, "lbl.height !== screenHeight - statusBar - navBarHeight");
|
||||
}
|
||||
|
||||
export function test_correct_layout_scrollable_content_true_bottom_edge_does_not_span() {
|
||||
const page = new Page();
|
||||
(<any>page).scrollableContent = true;
|
||||
page.actionBar.title = "ActionBar";
|
||||
|
||||
const tabView = new TabView();
|
||||
const tabItem = new TabViewItem();
|
||||
tabItem.title = "Item";
|
||||
const lbl = new Label();
|
||||
(<any>lbl).scrollableContent = true;
|
||||
tabItem.view = lbl;
|
||||
tabView.items = [tabItem];
|
||||
page.content = tabView;
|
||||
|
||||
helper.navigate(() => page);
|
||||
TKUnit.assertTrue(page.isLoaded, "page NOT loaded!");
|
||||
TKUnit.assertNotNull(lbl.viewController);
|
||||
(<UIViewController>lbl.viewController).edgesForExtendedLayout = UIRectEdge.Top | UIRectEdge.Left | UIRectEdge.Right;
|
||||
lbl.requestLayout();
|
||||
(<UIView>lbl.nativeViewProtected).setNeedsLayout();
|
||||
(<UIView>lbl.nativeViewProtected).layoutIfNeeded();
|
||||
TKUnit.waitUntilReady(() => lbl.isLayoutValid);
|
||||
const tabBarHeight = uiUtils.ios.getActualHeight(tabView.viewController.tabBar);
|
||||
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
|
||||
|
||||
const pageHeight = getHeight(page);
|
||||
TKUnit.assertEqual(pageHeight, screenHeight, "page.height !== screenHeight");
|
||||
|
||||
const contentHeight = getHeight(lbl);
|
||||
TKUnit.assertEqual(contentHeight, screenHeight - tabBarHeight, "lbl.height !== screenHeight - tabBarHeight");
|
||||
}
|
||||
|
||||
export function test_correct_layout_top_bottom_edges_does_not_span() {
|
||||
const page = new Page();
|
||||
page.actionBar.flat = false;
|
||||
(<any>page).scrollableContent = false;
|
||||
page.actionBar.title = "ActionBar";
|
||||
(<UIViewController>page.viewController).edgesForExtendedLayout = UIRectEdge.Left | UIRectEdge.Right;
|
||||
|
||||
const tabView = new TabView();
|
||||
(<UIViewController>tabView.viewController).edgesForExtendedLayout = UIRectEdge.Left | UIRectEdge.Right;
|
||||
const tabItem = new TabViewItem();
|
||||
tabItem.title = "Item";
|
||||
const lbl = new Label();
|
||||
tabItem.view = lbl;
|
||||
tabView.items = [tabItem];
|
||||
|
||||
page.content = tabView;
|
||||
helper.navigate(() => page);
|
||||
TKUnit.assertTrue(page.isLoaded, "page NOT loaded!");
|
||||
|
||||
(<UIViewController>lbl.viewController).edgesForExtendedLayout = UIRectEdge.Left | UIRectEdge.Right;
|
||||
lbl.requestLayout();
|
||||
(<UIView>lbl.nativeViewProtected).setNeedsLayout();
|
||||
(<UIView>lbl.nativeViewProtected).layoutIfNeeded();
|
||||
TKUnit.waitUntilReady(() => lbl.isLayoutValid);
|
||||
|
||||
const statusBarHeight = uiUtils.ios.getStatusBarHeight(page.viewController);
|
||||
const tabBarHeight = uiUtils.ios.getActualHeight(tabView.viewController.tabBar);
|
||||
const screenHeight = layout.toDevicePixels(UIScreen.mainScreen.bounds.size.height);
|
||||
const navBarHeight = uiUtils.ios.getActualHeight(page.frame.ios.controller.navigationBar);
|
||||
|
||||
const assert = (scrollable: boolean, flat: boolean) => {
|
||||
page.actionBar.flat = flat;
|
||||
(<any>page).scrollableContent = scrollable;
|
||||
(<any>lbl).scrollableContent = scrollable;
|
||||
|
||||
lbl.requestLayout();
|
||||
TKUnit.waitUntilReady(() => lbl.isLayoutValid);
|
||||
|
||||
const pageHeight = getHeight(page);
|
||||
TKUnit.assertEqual(pageHeight, screenHeight - statusBarHeight - navBarHeight, "page.height !== screenHeight - statusBarHeight - navBarHeight");
|
||||
const contentHeight = getHeight(lbl);
|
||||
TKUnit.assertEqual(contentHeight, screenHeight - statusBarHeight - navBarHeight - tabBarHeight, "lbl.height !== screenHeight - statusBarHeight - navBarHeight - tabBarHeight");
|
||||
};
|
||||
|
||||
// scrollable: false, flat: false;
|
||||
assert(false, false);
|
||||
// scrollable: true, flat: false;
|
||||
assert(true, false);
|
||||
// scrollable: true, flat: true;
|
||||
assert(true, true);
|
||||
// scrollable: false, flat: true;
|
||||
assert(false, true);
|
||||
}
|
||||
|
||||
export function test_showing_native_viewcontroller_doesnt_throw_exception() {
|
||||
@ -133,7 +370,7 @@ export function test_showing_native_viewcontroller_doesnt_throw_exception() {
|
||||
TKUnit.assertEqual(0, navigatedFrom, "navigatingTo");
|
||||
|
||||
let page = new Page();
|
||||
let navcontroller = <UINavigationController>frame.topmost().ios.controller;
|
||||
let navcontroller = <UINavigationController>topmost().ios.controller;
|
||||
|
||||
let completed = false;
|
||||
navcontroller.presentViewControllerAnimatedCompletion(page.ios, false, () => completed = true);
|
||||
@ -159,4 +396,4 @@ export function test_showing_native_viewcontroller_doesnt_throw_exception() {
|
||||
TKUnit.assertEqual(0, navigatingFrom, "navigatingTo");
|
||||
TKUnit.assertEqual(0, unloaded, "navigatingTo");
|
||||
TKUnit.assertEqual(0, navigatedFrom, "navigatingTo");
|
||||
}
|
||||
}
|
@ -104,7 +104,9 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
|
||||
public test_scrollToVerticalOffset_no_animation() {
|
||||
this.waitUntilTestElementLayoutIsValid();
|
||||
|
||||
TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
|
||||
// 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);
|
||||
TKUnit.assertAreClose(layoutHelper.dip(this.testView.verticalOffset), 100, 0.1, "this.testView.verticalOffset");
|
||||
}
|
||||
@ -112,11 +114,15 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
|
||||
public test_scrollToVerticalOffset_with_animation() {
|
||||
this.waitUntilTestElementLayoutIsValid();
|
||||
|
||||
TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
|
||||
// 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);
|
||||
|
||||
// No synchronous change.
|
||||
TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
|
||||
// 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); });
|
||||
|
||||
|
@ -39,12 +39,12 @@ function _createListView(): ListView {
|
||||
var button = <Button>args.view;
|
||||
if (!button) {
|
||||
button = new Button();
|
||||
button.on(Button.tapEvent, _clickHandlerFactory(args.index));
|
||||
args.view = button;
|
||||
}
|
||||
|
||||
button.text = "Button" + args.index;
|
||||
button.id = button.text;
|
||||
button.on(Button.tapEvent, _clickHandlerFactory(args.index));
|
||||
});
|
||||
|
||||
listView.items = items;
|
||||
@ -246,6 +246,9 @@ function _clickTheFirstButtonInTheListViewNatively(tabView: TabView) {
|
||||
button.performClick();
|
||||
}
|
||||
else {
|
||||
(<UIButton>(<UITableView>tabView.ios.viewControllers[0].view.subviews[0]).cellForRowAtIndexPath(NSIndexPath.indexPathForItemInSection(0, 0)).contentView.subviews[0]).sendActionsForControlEvents(UIControlEvents.TouchUpInside);
|
||||
const tableView = <UITableView>tabView.ios.viewControllers[0].view;
|
||||
const cell = <UITableViewCell>tableView.cellForRowAtIndexPath(NSIndexPath.indexPathForItemInSection(0, 0));
|
||||
const btn = <UIButton>cell.contentView.subviews[0];
|
||||
btn.sendActionsForControlEvents(UIControlEvents.TouchUpInside);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
import testModule = require("../../ui-test");
|
||||
import { UITest } from "../../ui-test";
|
||||
import { Label } from "tns-core-modules/ui/label";
|
||||
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
|
||||
import { unsetValue } from "tns-core-modules/ui/core/view";
|
||||
import TKUnit = require("../../TKUnit");
|
||||
import helper = require("../helper");
|
||||
import labelModule = require("tns-core-modules/ui/label");
|
||||
import stackLayoutModule = require("tns-core-modules/ui/layouts/stack-layout");
|
||||
import tabViewTestsNative = require("./tab-view-tests-native");
|
||||
import { unsetValue } from "tns-core-modules/ui/core/view";
|
||||
|
||||
// Using a TabView requires the "ui/tab-view" module.
|
||||
// >> article-require-tabview-module
|
||||
import * as tabViewModule from "tns-core-modules/ui/tab-view";
|
||||
// << article-require-tabview-module
|
||||
|
||||
export class TabViewTest extends testModule.UITest<tabViewModule.TabView> {
|
||||
export class TabViewTest extends UITest<tabViewModule.TabView> {
|
||||
|
||||
public create(): tabViewModule.TabView {
|
||||
// >> article-create-tabview
|
||||
@ -30,7 +30,7 @@ export class TabViewTest extends testModule.UITest<tabViewModule.TabView> {
|
||||
_createItems(count: number): Array<tabViewModule.TabViewItem> {
|
||||
var items = new Array<tabViewModule.TabViewItem>();
|
||||
for (var i = 0; i < count; i++) {
|
||||
var label = new labelModule.Label();
|
||||
var label = new Label();
|
||||
label.text = "Tab " + i;
|
||||
var tabEntry = new tabViewModule.TabViewItem();
|
||||
tabEntry.title = "Tab " + i;
|
||||
@ -80,16 +80,16 @@ export class TabViewTest extends testModule.UITest<tabViewModule.TabView> {
|
||||
var tabView = this.testView;
|
||||
// >> article-binding-tabview-items
|
||||
var items = [];
|
||||
var StackLayout0 = new stackLayoutModule.StackLayout();
|
||||
var label0 = new labelModule.Label();
|
||||
var StackLayout0 = new StackLayout();
|
||||
var label0 = new Label();
|
||||
label0.text = "Tab 0";
|
||||
StackLayout0.addChild(label0);
|
||||
var tabEntry0 = new tabViewModule.TabViewItem();
|
||||
tabEntry0.title = "Tab 0";
|
||||
tabEntry0.view = StackLayout0;
|
||||
items.push(tabEntry0);
|
||||
var StackLayout1 = new stackLayoutModule.StackLayout();
|
||||
var label1 = new labelModule.Label();
|
||||
var StackLayout1 = new StackLayout();
|
||||
var label1 = new Label();
|
||||
label1.text = "Tab 1";
|
||||
StackLayout1.addChild(label1);
|
||||
var tabEntry1 = new tabViewModule.TabViewItem();
|
||||
@ -168,24 +168,16 @@ export class TabViewTest extends testModule.UITest<tabViewModule.TabView> {
|
||||
TKUnit.assertEqual(actualValue, expectedValue, "selectedIndex");
|
||||
}
|
||||
|
||||
// public testSettingNegativeSelectedIndexShouldThrow = function () {
|
||||
// var tabView = this.testView;
|
||||
// this.waitUntilTestElementIsLoaded();
|
||||
// tabView.items = this._createItems(10);
|
||||
public testSettingNegativeSelectedIndexShouldThrow() {
|
||||
var tabView = this.testView;
|
||||
tabView.items = this._createItems(3);
|
||||
this.waitUntilTestElementIsLoaded();
|
||||
|
||||
// TKUnit.assertThrows(function () {
|
||||
// tabView.selectedIndex = -1;
|
||||
// }, "Setting selectedIndex to a negative number should throw.");
|
||||
// }
|
||||
|
||||
// public testSettingSelectedIndexLargerThanCountShouldThrow = function () {
|
||||
// var tabView = this.testView;
|
||||
// this.waitUntilTestElementIsLoaded();
|
||||
// tabView.items = this._createItems(10);
|
||||
// TKUnit.assertThrows(function () {
|
||||
// tabView.selectedIndex = 10;
|
||||
// }, "Setting selectedIndex to a number bigger than items count should throw.");
|
||||
// }
|
||||
tabView.items.forEach((item, index, array) => {
|
||||
const expected = index === tabView.selectedIndex;
|
||||
TKUnit.assertEqual(item.isLoaded, expected);
|
||||
});
|
||||
}
|
||||
|
||||
public testBindingToTabEntryWithUndefinedViewShouldThrow = function () {
|
||||
var tabView = this.testView;
|
||||
|
@ -124,6 +124,8 @@ setApplication(androidApp);
|
||||
|
||||
let mainEntry: NavigationEntry;
|
||||
let started = false;
|
||||
// NOTE: for backwards compatibility. Remove for 4.0.0.
|
||||
let createRootFrame = true;
|
||||
export function start(entry?: NavigationEntry | string) {
|
||||
if (started) {
|
||||
throw new Error("Application is already started.");
|
||||
@ -137,6 +139,15 @@ export function start(entry?: NavigationEntry | string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldCreateRootFrame(): boolean {
|
||||
return createRootFrame;
|
||||
}
|
||||
|
||||
export function run(entry?: NavigationEntry | string) {
|
||||
createRootFrame = false;
|
||||
start(entry);
|
||||
}
|
||||
|
||||
export function getMainEntry() {
|
||||
return mainEntry;
|
||||
}
|
||||
|
15
tns-core-modules/application/application.d.ts
vendored
15
tns-core-modules/application/application.d.ts
vendored
@ -167,10 +167,25 @@ export function on(event: "livesync", callback: (args: EventData) => void);
|
||||
export function off(eventNames: string, callback?: any, thisArg?: any);
|
||||
|
||||
/**
|
||||
* Deprecated. Use application run.
|
||||
* Call this method to start the application. Important: All code after this method call will not be executed!
|
||||
*/
|
||||
export function start(entry?: NavigationEntry | string);
|
||||
|
||||
/**
|
||||
* Call this method to run the application. Important: All code after this method call will not be executed!
|
||||
* Compared to start this method won't create Frame as root view.
|
||||
*/
|
||||
export function run(entry?: NavigationEntry | string);
|
||||
|
||||
//@private
|
||||
/**
|
||||
* Internal method use to check if a root Frame should be automatically created as root view.
|
||||
* @private
|
||||
*/
|
||||
export function shouldCreateRootFrame(): boolean;
|
||||
//@endprivate
|
||||
|
||||
/**
|
||||
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
|
||||
* @param eventNames - String corresponding to events (e.g. "onLaunch"). Optionally could be used more events separated by `,` (e.g. "onLaunch", "onSuspend").
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
// First reexport so that app module is initialized.
|
||||
export * from "./application-common";
|
||||
|
||||
import { Frame, View, NavigationEntry } from "../ui/frame";
|
||||
import { ios as iosView } from "../ui/core/view";
|
||||
import { Frame, View, NavigationEntry, loadViewFromEntry } from "../ui/frame";
|
||||
import { ios } from "../ui/utils";
|
||||
import * as utils from "../utils/utils";
|
||||
import { profile } from "../profiling";
|
||||
@ -25,27 +26,6 @@ class Responder extends UIResponder {
|
||||
|
||||
let displayedOnce = false;
|
||||
|
||||
class Window extends UIWindow {
|
||||
public content;
|
||||
|
||||
initWithFrame(frame: CGRect): this {
|
||||
const window = <this>super.initWithFrame(frame);
|
||||
if (window) {
|
||||
window.autoresizingMask = UIViewAutoresizing.None;
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
@profile
|
||||
public layoutSubviews(): void {
|
||||
if (utils.ios.MajorVersion < 9) {
|
||||
ios._layoutRootView(this.content, utils.ios.getter(UIScreen, UIScreen.mainScreen).bounds);
|
||||
} else {
|
||||
ios._layoutRootView(this.content, this.frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationObserver extends NSObject {
|
||||
private _onReceiveCallback: (notification: NSNotification) => void;
|
||||
|
||||
@ -65,11 +45,9 @@ class NotificationObserver extends NSObject {
|
||||
}
|
||||
|
||||
class IOSApplication implements IOSApplicationDefinition {
|
||||
public rootController: any;
|
||||
|
||||
private _delegate: typeof UIApplicationDelegate;
|
||||
private _currentOrientation = utils.ios.getter(UIDevice, UIDevice.currentDevice).orientation;
|
||||
private _window: Window;
|
||||
private _window: UIWindow;
|
||||
private _observers: Array<NotificationObserver>;
|
||||
|
||||
constructor() {
|
||||
@ -82,11 +60,15 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
this.addNotificationObserver(UIDeviceOrientationDidChangeNotification, this.orientationDidChange.bind(this));
|
||||
}
|
||||
|
||||
get rootController(): UIViewController {
|
||||
return this._window.rootViewController;
|
||||
}
|
||||
|
||||
get nativeApp(): UIApplication {
|
||||
return utils.ios.getter(UIApplication, UIApplication.sharedApplication);
|
||||
}
|
||||
|
||||
get window(): Window {
|
||||
get window(): UIWindow {
|
||||
return this._window;
|
||||
}
|
||||
|
||||
@ -116,7 +98,8 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
|
||||
@profile
|
||||
private didFinishLaunchingWithOptions(notification: NSNotification) {
|
||||
this._window = <Window>Window.alloc().initWithFrame(utils.ios.getter(UIScreen, UIScreen.mainScreen).bounds);
|
||||
this._window = UIWindow.alloc().initWithFrame(utils.ios.getter(UIScreen, UIScreen.mainScreen).bounds);
|
||||
// TODO: Expose Window module so that it can we styled from XML & CSS
|
||||
this._window.backgroundColor = utils.ios.getter(UIColor, UIColor.whiteColor);
|
||||
|
||||
const args: LaunchEventData = {
|
||||
@ -128,31 +111,16 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
notify(args);
|
||||
notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: <any>this, cssFile: getCssFileName() });
|
||||
|
||||
let rootView = createRootView(args.root);
|
||||
this._window.content = rootView;
|
||||
|
||||
if (rootView instanceof Frame) {
|
||||
this.rootController = this._window.rootViewController = rootView.ios.controller;
|
||||
}
|
||||
else if (rootView.ios instanceof UIViewController) {
|
||||
this.rootController = this._window.rootViewController = rootView.ios;
|
||||
}
|
||||
else if (rootView.ios instanceof UIView) {
|
||||
let newController = UIViewController.new();
|
||||
newController.view.addSubview(rootView.ios);
|
||||
this.rootController = newController;
|
||||
}
|
||||
else {
|
||||
throw new Error("Root should be either UIViewController or UIView");
|
||||
}
|
||||
|
||||
const rootView = createRootView(args.root);
|
||||
const controller = getViewController(rootView);
|
||||
this._window.rootViewController = controller;
|
||||
this._window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
@profile
|
||||
private didBecomeActive(notification: NSNotification) {
|
||||
let ios = utils.ios.getter(UIApplication, UIApplication.sharedApplication);
|
||||
let object = this;
|
||||
const ios = utils.ios.getter(UIApplication, UIApplication.sharedApplication);
|
||||
const object = this;
|
||||
notify(<ApplicationEventData>{ eventName: resumeEvent, object, ios });
|
||||
if (!displayedOnce) {
|
||||
notify(<ApplicationEventData>{ eventName: displayedEvent, object, ios });
|
||||
@ -210,20 +178,20 @@ setApplication(iosApp);
|
||||
let mainEntry: NavigationEntry;
|
||||
function createRootView(v?: View) {
|
||||
let rootView = v;
|
||||
let frame: Frame;
|
||||
let main: string | NavigationEntry;
|
||||
if (!rootView) {
|
||||
// try to navigate to the mainEntry (if specified)
|
||||
main = mainEntry;
|
||||
if (main) {
|
||||
frame = new Frame();
|
||||
frame.navigate(main);
|
||||
if (mainEntry) {
|
||||
if (createRootFrame) {
|
||||
const frame = new Frame();
|
||||
rootView = frame;
|
||||
frame.navigate(mainEntry);
|
||||
} else {
|
||||
rootView = loadViewFromEntry(mainEntry);
|
||||
}
|
||||
} else {
|
||||
// TODO: Throw an exception?
|
||||
throw new Error("A Frame must be used to navigate to a Page.");
|
||||
}
|
||||
|
||||
rootView = frame;
|
||||
}
|
||||
|
||||
rootView._setupAsRootView({});
|
||||
@ -234,6 +202,8 @@ export function getMainEntry() {
|
||||
return mainEntry;
|
||||
}
|
||||
|
||||
// NOTE: for backwards compatibility. Remove for 4.0.0.
|
||||
let createRootFrame = true;
|
||||
let started: boolean = false;
|
||||
export function start(entry?: string | NavigationEntry) {
|
||||
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
|
||||
@ -243,29 +213,47 @@ export function start(entry?: string | NavigationEntry) {
|
||||
// Normal NativeScript app will need UIApplicationMain.
|
||||
UIApplicationMain(0, null, null, iosApp && iosApp.delegate ? NSStringFromClass(<any>iosApp.delegate) : NSStringFromClass(Responder));
|
||||
} else {
|
||||
let rootView = createRootView();
|
||||
const rootView = createRootView();
|
||||
if (rootView) {
|
||||
// Attach to the existing iOS app
|
||||
var window = iosApp.nativeApp.keyWindow || (iosApp.nativeApp.windows.count > 0 && iosApp.nativeApp.windows[0]);
|
||||
const window = iosApp.nativeApp.keyWindow || (iosApp.nativeApp.windows.count > 0 && iosApp.nativeApp.windows[0]);
|
||||
if (window) {
|
||||
var rootController = window.rootViewController;
|
||||
const rootController = window.rootViewController;
|
||||
if (rootController) {
|
||||
rootController.presentViewControllerAnimatedCompletion(rootView.ios.controller, true, null);
|
||||
ios._layoutRootView(rootView, utils.ios.getter(UIScreen, UIScreen.mainScreen).bounds);
|
||||
const controller = getViewController(rootView);
|
||||
rootController.presentViewControllerAnimatedCompletion(controller, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function run(entry?: string | NavigationEntry) {
|
||||
createRootFrame = false;
|
||||
start(entry);
|
||||
}
|
||||
|
||||
export function getNativeApplication(): UIApplication {
|
||||
return iosApp.nativeApp;
|
||||
}
|
||||
|
||||
function getViewController(view: View): UIViewController {
|
||||
let viewController = view.viewController || view.ios;
|
||||
if (viewController instanceof UIViewController) {
|
||||
return viewController;
|
||||
} else if (view.ios instanceof UIView) {
|
||||
viewController = iosView.UILayoutViewController.initWithOwner(new WeakRef(view));
|
||||
viewController.view = view.ios;
|
||||
return viewController;
|
||||
} else {
|
||||
throw new Error("Root should be either UIViewController or UIView");
|
||||
}
|
||||
}
|
||||
|
||||
global.__onLiveSync = function () {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
|
||||
livesync();
|
||||
}
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ function onTitlePropertyChanged(actionBar: ActionBarBase, oldValue: string, newV
|
||||
actionBar._onTitlePropertyChanged();
|
||||
}
|
||||
|
||||
let titleProperty = new Property<ActionBarBase, string>({ name: "title", valueChanged: onTitlePropertyChanged });
|
||||
export const titleProperty = new Property<ActionBarBase, string>({ name: "title", valueChanged: onTitlePropertyChanged });
|
||||
titleProperty.register(ActionBarBase);
|
||||
|
||||
function onItemChanged(item: ActionItemBase, oldValue: string, newValue: string) {
|
||||
@ -323,13 +323,13 @@ function onItemChanged(item: ActionItemBase, oldValue: string, newValue: string)
|
||||
}
|
||||
}
|
||||
|
||||
let textProperty = new Property<ActionItemBase, string>({ name: "text", defaultValue: "", valueChanged: onItemChanged });
|
||||
export const textProperty = new Property<ActionItemBase, string>({ name: "text", defaultValue: "", valueChanged: onItemChanged });
|
||||
textProperty.register(ActionItemBase);
|
||||
|
||||
let iconProperty = new Property<ActionItemBase, string>({ name: "icon", valueChanged: onItemChanged });
|
||||
export const iconProperty = new Property<ActionItemBase, string>({ name: "icon", valueChanged: onItemChanged });
|
||||
iconProperty.register(ActionItemBase);
|
||||
|
||||
let visibilityProperty = new Property({ name: "visibility", defaultValue: "visible", valueChanged: onItemChanged });
|
||||
export const visibilityProperty = new Property({ name: "visibility", defaultValue: "visible", valueChanged: onItemChanged });
|
||||
visibilityProperty.register(ActionItemBase);
|
||||
|
||||
export const flatProperty = new Property<ActionBarBase, boolean>({ name: "flat", defaultValue: false, valueConverter: booleanConverter });
|
||||
|
@ -66,11 +66,9 @@ export class ActionBar extends ActionBarBase {
|
||||
public _addChildFromBuilder(name: string, value: any) {
|
||||
if (value instanceof NavigationButton) {
|
||||
this.navigationButton = value;
|
||||
}
|
||||
else if (value instanceof ActionItem) {
|
||||
} else if (value instanceof ActionItem) {
|
||||
this.actionItems.addItem(value);
|
||||
}
|
||||
else if (value instanceof View) {
|
||||
} else if (value instanceof View) {
|
||||
this.titleView = value;
|
||||
}
|
||||
}
|
||||
@ -98,7 +96,7 @@ export class ActionBar extends ActionBarBase {
|
||||
}
|
||||
|
||||
// Find previous ViewController in the navigation stack
|
||||
let indexOfViewController = navController.viewControllers.indexOfObject(viewController);
|
||||
const indexOfViewController = navController.viewControllers.indexOfObject(viewController);
|
||||
if (indexOfViewController < navController.viewControllers.count && indexOfViewController > 0) {
|
||||
previousController = navController.viewControllers[indexOfViewController - 1];
|
||||
}
|
||||
@ -109,8 +107,7 @@ export class ActionBar extends ActionBarBase {
|
||||
let tapHandler = TapBarItemHandlerImpl.initWithOwner(new WeakRef(this.navigationButton));
|
||||
let barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction(this.navigationButton.text + "", UIBarButtonItemStyle.Plain, tapHandler, "tap");
|
||||
previousController.navigationItem.backBarButtonItem = barButtonItem;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
previousController.navigationItem.backBarButtonItem = null;
|
||||
}
|
||||
}
|
||||
@ -128,8 +125,7 @@ export class ActionBar extends ActionBarBase {
|
||||
let image = img.ios.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
|
||||
navigationBar.backIndicatorImage = image;
|
||||
navigationBar.backIndicatorTransitionMaskImage = image;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
navigationBar.backIndicatorImage = null;
|
||||
navigationBar.backIndicatorTransitionMaskImage = null;
|
||||
}
|
||||
@ -150,15 +146,14 @@ export class ActionBar extends ActionBarBase {
|
||||
}
|
||||
|
||||
private populateMenuItems(navigationItem: UINavigationItem) {
|
||||
let items = this.actionItems.getVisibleItems();
|
||||
let leftBarItems = [];
|
||||
let rightBarItems = [];
|
||||
const items = this.actionItems.getVisibleItems();
|
||||
const leftBarItems = [];
|
||||
const rightBarItems = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let barButtonItem = this.createBarButtonItem(items[i]);
|
||||
const barButtonItem = this.createBarButtonItem(items[i]);
|
||||
if (items[i].ios.position === "left") {
|
||||
leftBarItems.push(barButtonItem);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
rightBarItems.splice(0, 0, barButtonItem);
|
||||
}
|
||||
}
|
||||
@ -171,7 +166,7 @@ export class ActionBar extends ActionBarBase {
|
||||
}
|
||||
|
||||
private createBarButtonItem(item: ActionItemDefinition): UIBarButtonItem {
|
||||
let tapHandler = TapBarItemHandlerImpl.initWithOwner(new WeakRef(item));
|
||||
const tapHandler = TapBarItemHandlerImpl.initWithOwner(new WeakRef(item));
|
||||
// associate handler with menuItem or it will get collected by JSC.
|
||||
(<any>item).handler = tapHandler;
|
||||
|
||||
@ -180,24 +175,21 @@ export class ActionBar extends ActionBarBase {
|
||||
let recognizer = UITapGestureRecognizer.alloc().initWithTargetAction(tapHandler, "tap");
|
||||
item.actionView.ios.addGestureRecognizer(recognizer);
|
||||
barButtonItem = UIBarButtonItem.alloc().initWithCustomView(item.actionView.ios);
|
||||
}
|
||||
else if (item.ios.systemIcon !== undefined) {
|
||||
} else if (item.ios.systemIcon !== undefined) {
|
||||
let id: number = item.ios.systemIcon;
|
||||
if (typeof id === "string") {
|
||||
id = parseInt(id);
|
||||
}
|
||||
|
||||
barButtonItem = UIBarButtonItem.alloc().initWithBarButtonSystemItemTargetAction(id, tapHandler, "tap");
|
||||
}
|
||||
else if (item.icon) {
|
||||
let img = fromFileOrResource(item.icon);
|
||||
} else if (item.icon) {
|
||||
const img = fromFileOrResource(item.icon);
|
||||
if (img && img.ios) {
|
||||
barButtonItem = UIBarButtonItem.alloc().initWithImageStyleTargetAction(img.ios, UIBarButtonItemStyle.Plain, tapHandler, "tap");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw new Error("Error loading icon from " + item.icon);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction(item.text + "", UIBarButtonItemStyle.Plain, tapHandler, "tap");
|
||||
}
|
||||
|
||||
@ -211,17 +203,16 @@ export class ActionBar extends ActionBarBase {
|
||||
}
|
||||
|
||||
private updateColors(navBar: UINavigationBar) {
|
||||
let color = this.color;
|
||||
const color = this.color;
|
||||
if (color) {
|
||||
navBar.titleTextAttributes = <any>{ [NSForegroundColorAttributeName]: color.ios };
|
||||
navBar.tintColor = color.ios;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
navBar.titleTextAttributes = null;
|
||||
navBar.tintColor = null;
|
||||
}
|
||||
|
||||
let bgColor = <Color>this.backgroundColor;
|
||||
const bgColor = <Color>this.backgroundColor;
|
||||
navBar.barTintColor = bgColor ? bgColor.ios : null;
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,11 @@ export abstract class ViewBase extends Observable {
|
||||
|
||||
public ios: any;
|
||||
public android: any;
|
||||
|
||||
/**
|
||||
* returns the native UIViewController.
|
||||
*/
|
||||
public viewController: any;
|
||||
|
||||
/**
|
||||
* read-only. If you want to set out-of-band the nativeView use the setNativeView method.
|
||||
|
@ -155,6 +155,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
public domNode: dnm.DOMNode;
|
||||
|
||||
public recycleNativeView: "always" | "never" | "auto";
|
||||
public viewController: any;
|
||||
public bindingContext: any;
|
||||
public nativeViewProtected: any;
|
||||
public parent: ViewBase;
|
||||
@ -462,7 +463,8 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
|
||||
@profile
|
||||
public requestLayout(): void {
|
||||
let parent = this.parent;
|
||||
// Default implementation for non View instances (like TabViewItem).
|
||||
const parent = this.parent;
|
||||
if (parent) {
|
||||
parent.requestLayout();
|
||||
}
|
||||
@ -645,9 +647,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
} else {
|
||||
// TODO: Implement _createNativeView for iOS
|
||||
nativeView = this.createNativeView();
|
||||
if (nativeView) {
|
||||
this._iosView = nativeView;
|
||||
}
|
||||
this._iosView = nativeView || this.nativeViewProtected;
|
||||
}
|
||||
|
||||
// This will account for nativeView that is created in createNativeView, recycled
|
||||
@ -655,7 +655,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
this.setNativeView(nativeView || this.nativeViewProtected);
|
||||
|
||||
if (this.parent) {
|
||||
let nativeIndex = this.parent._childIndexToNativeChildIndex(atIndex);
|
||||
const nativeIndex = this.parent._childIndexToNativeChildIndex(atIndex);
|
||||
this._isAddedToNativeVisualTree = this.parent._addViewToNativeVisualTree(this, nativeIndex);
|
||||
}
|
||||
|
||||
|
@ -683,10 +683,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
return callback(this);
|
||||
}
|
||||
|
||||
public _updateLayout() {
|
||||
// needed for iOS.
|
||||
}
|
||||
|
||||
public focus(): boolean {
|
||||
return undefined;
|
||||
}
|
||||
|
18
tns-core-modules/ui/core/view/view.d.ts
vendored
18
tns-core-modules/ui/core/view/view.d.ts
vendored
@ -326,7 +326,7 @@ export abstract class View extends ViewBase {
|
||||
* @param r Right position, relative to parent
|
||||
* @param b Bottom position, relative to parent
|
||||
*/
|
||||
public layout(left: number, top: number, right: number, bottom: number): void;
|
||||
public layout(left: number, top: number, right: number, bottom: number, setFrame?: boolean): void;
|
||||
|
||||
/**
|
||||
* Returns the raw width component.
|
||||
@ -340,11 +340,6 @@ export abstract class View extends ViewBase {
|
||||
|
||||
public getMeasuredState(): number;
|
||||
|
||||
/**
|
||||
* Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree.
|
||||
*/
|
||||
public requestLayout(): void;
|
||||
|
||||
/**
|
||||
* Measure the view and its content to determine the measured width and the measured height. This method is invoked by measure(int, int) and should be overriden by subclasses to provide accurate and efficient measurement of their contents.
|
||||
* When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height of this view. Failure to do so will trigger an exception, thrown by measure(int, int).
|
||||
@ -502,10 +497,6 @@ export abstract class View extends ViewBase {
|
||||
* @private
|
||||
*/
|
||||
_setNativeClipToBounds(): void;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateLayout(): void;
|
||||
/**
|
||||
* Called by measure method to cache measureSpecs.
|
||||
* @private
|
||||
@ -648,3 +639,10 @@ export const originXProperty: Property<View, number>;
|
||||
export const originYProperty: Property<View, number>;
|
||||
export const isEnabledProperty: Property<View, boolean>;
|
||||
export const isUserInteractionEnabledProperty: Property<View, boolean>;
|
||||
|
||||
export namespace ios {
|
||||
export function layoutView(controller: UIViewController, owner: View): void;
|
||||
export class UILayoutViewController extends UIViewController {
|
||||
public static initWithOwner(owner: WeakRef<View>): UILayoutViewController;
|
||||
}
|
||||
}
|
@ -6,7 +6,10 @@ import {
|
||||
traceEnabled, traceWrite, traceCategories
|
||||
} from "./view-common";
|
||||
|
||||
import { ios, Background } from "../../styling/background";
|
||||
import { ios as iosBackground, Background } from "../../styling/background";
|
||||
// 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";
|
||||
import {
|
||||
Visibility,
|
||||
visibilityProperty, opacityProperty,
|
||||
@ -62,9 +65,16 @@ export class View extends ViewCommon {
|
||||
super.requestLayout();
|
||||
this._privateFlags |= PFLAG_FORCE_LAYOUT;
|
||||
|
||||
let parent = <View>this.parent;
|
||||
if (parent && !parent.isLayoutRequested) {
|
||||
parent.requestLayout();
|
||||
const parent = <View>this.parent;
|
||||
if (parent) {
|
||||
if (!parent.isLayoutRequested) {
|
||||
parent.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
const nativeView = this.nativeViewProtected;
|
||||
if (nativeView && this.isLoaded) {
|
||||
nativeView.setNeedsLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,9 +99,12 @@ export class View extends ViewCommon {
|
||||
}
|
||||
|
||||
@profile
|
||||
public layout(left: number, top: number, right: number, bottom: number): void {
|
||||
public layout(left: number, top: number, right: number, bottom: number, setFrame = true): void {
|
||||
let { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
|
||||
this.layoutNativeView(left, top, right, bottom);
|
||||
if (setFrame) {
|
||||
this.layoutNativeView(left, top, right, bottom);
|
||||
}
|
||||
|
||||
if (boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED) {
|
||||
this.onLayout(left, top, right, bottom);
|
||||
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
|
||||
@ -168,17 +181,11 @@ export class View extends ViewCommon {
|
||||
return;
|
||||
}
|
||||
|
||||
let nativeView = this.nativeViewProtected;
|
||||
|
||||
let frame = CGRectMake(layout.toDeviceIndependentPixels(left), layout.toDeviceIndependentPixels(top), layout.toDeviceIndependentPixels(right - left), layout.toDeviceIndependentPixels(bottom - top));
|
||||
const nativeView = this.nativeViewProtected;
|
||||
const frame = CGRectMake(layout.toDeviceIndependentPixels(left), layout.toDeviceIndependentPixels(top), layout.toDeviceIndependentPixels(right - left), layout.toDeviceIndependentPixels(bottom - top));
|
||||
this._setNativeViewFrame(nativeView, frame);
|
||||
}
|
||||
|
||||
public _updateLayout() {
|
||||
let oldBounds = this._getCurrentLayoutBounds();
|
||||
this.layoutNativeView(oldBounds.left, oldBounds.top, oldBounds.right, oldBounds.bottom);
|
||||
}
|
||||
|
||||
public focus(): boolean {
|
||||
if (this.ios) {
|
||||
return this.ios.becomeFirstResponder();
|
||||
@ -221,7 +228,7 @@ export class View extends ViewCommon {
|
||||
|
||||
let myPointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null);
|
||||
let otherPointInWindow = otherView.nativeViewProtected.convertPointToView(otherView.nativeViewProtected.bounds.origin, null);
|
||||
return {
|
||||
return {
|
||||
x: myPointInWindow.x - otherPointInWindow.x,
|
||||
y: myPointInWindow.y - otherPointInWindow.y
|
||||
};
|
||||
@ -425,7 +432,7 @@ export class View extends ViewCommon {
|
||||
if (value instanceof UIColor) {
|
||||
this.nativeViewProtected.backgroundColor = value;
|
||||
} else {
|
||||
ios.createBackgroundUIColor(this, (color: UIColor) => {
|
||||
iosBackground.createBackgroundUIColor(this, (color: UIColor) => {
|
||||
this.nativeViewProtected.backgroundColor = color;
|
||||
});
|
||||
this._setNativeClipToBounds();
|
||||
@ -457,10 +464,6 @@ export class CustomLayoutView extends View {
|
||||
this.nativeViewProtected = UIView.new();
|
||||
}
|
||||
|
||||
get ios(): UIView {
|
||||
return this.nativeViewProtected;
|
||||
}
|
||||
|
||||
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
||||
// Don't call super because it will set MeasureDimension. This method must be overriden and calculate its measuredDimensions.
|
||||
}
|
||||
@ -491,4 +494,102 @@ export class CustomLayoutView extends View {
|
||||
child.nativeViewProtected.removeFromSuperview();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isScrollable(controller: UIViewController, owner: View): boolean {
|
||||
let scrollable = (<any>owner).scrollableContent;
|
||||
if (scrollable === undefined) {
|
||||
if (controller.childViewControllers.count > 0) {
|
||||
scrollable = true;
|
||||
} else {
|
||||
let view = controller.view;
|
||||
while (view) {
|
||||
if (view instanceof UIScrollView) {
|
||||
scrollable = true;
|
||||
break;
|
||||
}
|
||||
|
||||
view = view.subviews.count > 0 ? view.subviews[0] : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scrollable === true || scrollable === "true";;
|
||||
}
|
||||
|
||||
function getStatusBarHeight(controller: UIViewController): number {
|
||||
let shouldReturnStatusBarHeight = false;
|
||||
if (controller.presentingViewController) {
|
||||
if (CGRectEqualToRect(controller.view.frame, controller.view.window.bounds)) {
|
||||
shouldReturnStatusBarHeight = true;
|
||||
}
|
||||
} else {
|
||||
shouldReturnStatusBarHeight = true;
|
||||
}
|
||||
|
||||
return shouldReturnStatusBarHeight ? uiUtils.ios.getStatusBarHeight(controller) : 0;
|
||||
}
|
||||
|
||||
export namespace ios {
|
||||
export function layoutView(controller: UIViewController, owner: View): void {
|
||||
const scrollableContent = isScrollable(controller, owner);
|
||||
const navController = controller.navigationController;
|
||||
const navBarVisible = navController && !navController.navigationBarHidden;
|
||||
const navBarTranslucent = navController ? navController.navigationBar.translucent : false;
|
||||
|
||||
let navBarHeight = navBarVisible ? uiUtils.ios.getActualHeight(navController.navigationBar) : 0;
|
||||
let statusBarHeight = getStatusBarHeight(controller);
|
||||
|
||||
const edgesForExtendedLayout = controller.edgesForExtendedLayout;
|
||||
const extendedLayoutIncludesOpaqueBars = controller.extendedLayoutIncludesOpaqueBars;
|
||||
const layoutExtendsOnTop = (edgesForExtendedLayout & UIRectEdge.Top) === UIRectEdge.Top;
|
||||
if (!layoutExtendsOnTop
|
||||
|| (!extendedLayoutIncludesOpaqueBars && !navBarTranslucent && navBarVisible)
|
||||
|| (scrollableContent && navBarVisible)) {
|
||||
navBarHeight = 0;
|
||||
statusBarHeight = 0;
|
||||
}
|
||||
|
||||
const tabBarController = controller.tabBarController;
|
||||
const layoutExtendsOnBottom = (edgesForExtendedLayout & UIRectEdge.Bottom) === UIRectEdge.Bottom;
|
||||
|
||||
let tabBarHeight = 0;
|
||||
const tabBarVisible = tabBarController && !tabBarController.tabBar.hidden;
|
||||
const tabBarTranslucent = tabBarController ? tabBarController.tabBar.translucent : false;
|
||||
|
||||
// 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;
|
||||
const width = layout.toDevicePixels(size.width);
|
||||
const height = layout.toDevicePixels(size.height - tabBarHeight);
|
||||
|
||||
const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY);
|
||||
const heightSpec = layout.makeMeasureSpec(height - statusBarHeight - navBarHeight, layout.EXACTLY);
|
||||
|
||||
owner.measure(widthSpec, heightSpec);
|
||||
|
||||
// Page.nativeView.frame is never set by our layout...
|
||||
owner.layout(0, statusBarHeight + navBarHeight, width, height, false);
|
||||
}
|
||||
|
||||
export class UILayoutViewController extends UIViewController {
|
||||
public owner: WeakRef<View>;
|
||||
|
||||
public static initWithOwner(owner: WeakRef<View>): UILayoutViewController {
|
||||
const controller = <UILayoutViewController>UILayoutViewController.new();
|
||||
controller.owner = owner;
|
||||
return controller;
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews(): void {
|
||||
super.viewDidLayoutSubviews();
|
||||
|
||||
const owner = this.owner.get();
|
||||
layoutView(this, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +126,11 @@ const pageFromBuilder = profile("pageFromBuilder", (moduleNamePath: string, modu
|
||||
return page;
|
||||
});
|
||||
|
||||
// TODO: Consider if we need this one or we could go with resolvePageFromEntry only!
|
||||
export function loadViewFromEntry(entry: NavigationEntry): View {
|
||||
return resolvePageFromEntry(entry);
|
||||
}
|
||||
|
||||
export const resolvePageFromEntry = profile("resolvePageFromEntry", (entry: NavigationEntry): Page => {
|
||||
let page: Page;
|
||||
|
||||
@ -169,6 +174,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
private _transition: NavigationTransition;
|
||||
private _backStack = new Array<BackstackEntry>();
|
||||
private _navigationQueue = new Array<NavigationContext>();
|
||||
private _paramToNavigate: any;
|
||||
|
||||
public _isInFrameStack = false;
|
||||
public static defaultAnimatedNavigation = true;
|
||||
@ -176,6 +182,22 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
|
||||
// TODO: Currently our navigation will not be synchronized in case users directly call native navigation methods like Activity.startActivity.
|
||||
|
||||
public _addChildFromBuilder(name: string, value: any) {
|
||||
if (value instanceof Page) {
|
||||
this.navigate({ create: () => value });
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public onLoaded() {
|
||||
super.onLoaded();
|
||||
|
||||
if (this._paramToNavigate) {
|
||||
this.navigate(this._paramToNavigate);
|
||||
this._paramToNavigate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public canGoBack(): boolean {
|
||||
return this._backStack.length > 0;
|
||||
}
|
||||
@ -248,6 +270,11 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
traceWrite(`NAVIGATE`, traceCategories.Navigation);
|
||||
}
|
||||
|
||||
if (!this.isLoaded) {
|
||||
this._paramToNavigate = param;
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = buildEntryFromArgs(param);
|
||||
const page = resolvePageFromEntry(entry);
|
||||
|
||||
|
@ -7,7 +7,10 @@ import { Page } from "../page";
|
||||
|
||||
// Types.
|
||||
import * as application from "../../application";
|
||||
import { FrameBase, NavigationContext, stack, goBack, View, Observable, traceEnabled, traceWrite, traceCategories } from "./frame-common";
|
||||
import {
|
||||
FrameBase, NavigationContext, stack, goBack, View, Observable, loadViewFromEntry,
|
||||
traceEnabled, traceWrite, traceCategories
|
||||
} from "./frame-common";
|
||||
import { DIALOG_FRAGMENT_TAG } from "../page/constants";
|
||||
import {
|
||||
_setAndroidFragmentTransitions, _onFragmentCreateAnimator,
|
||||
@ -85,7 +88,10 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
|
||||
public setCurrent(entry: BackstackEntry): void {
|
||||
this.changeCurrentPage(entry);
|
||||
if (this._currentEntry !== entry) {
|
||||
this.changeCurrentPage(entry);
|
||||
}
|
||||
|
||||
this._currentEntry = entry;
|
||||
this._isBack = true;
|
||||
this._processNavigationQueue(entry.resolvedPage);
|
||||
@ -134,7 +140,13 @@ export class Frame extends FrameBase {
|
||||
return;
|
||||
}
|
||||
|
||||
const manager = activity.getFragmentManager();
|
||||
let manager: android.app.FragmentManager;
|
||||
const dialogFragment = (<any>global).dialogFragment;
|
||||
if (dialogFragment) {
|
||||
manager = dialogFragment.getChildFragmentManager();
|
||||
} else {
|
||||
manager = activity.getFragmentManager();
|
||||
}
|
||||
|
||||
// Current Fragment
|
||||
const currentFragment = this._currentEntry ? manager.findFragmentByTag(this._currentEntry.fragmentTag) : null;
|
||||
@ -176,7 +188,14 @@ export class Frame extends FrameBase {
|
||||
navDepth = backstackEntry.navDepth;
|
||||
|
||||
const activity = this._android.activity;
|
||||
const manager = activity.getFragmentManager();
|
||||
let manager: android.app.FragmentManager;
|
||||
const dialogFragment = (<any>global).dialogFragment;
|
||||
if (dialogFragment) {
|
||||
manager = dialogFragment.getChildFragmentManager();
|
||||
} else {
|
||||
manager = activity.getFragmentManager();
|
||||
}
|
||||
|
||||
const transaction = manager.beginTransaction();
|
||||
|
||||
if (!backstackEntry.fragment) {
|
||||
@ -203,16 +222,15 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
|
||||
public createNativeView() {
|
||||
const root = new org.nativescript.widgets.ContentLayout(this._context);
|
||||
if (this._containerViewId < 0) {
|
||||
this._containerViewId = android.view.View.generateViewId();
|
||||
}
|
||||
return root;
|
||||
return new org.nativescript.widgets.ContentLayout(this._context);
|
||||
}
|
||||
|
||||
public initNativeView(): void {
|
||||
super.initNativeView();
|
||||
this._android.rootViewGroup = this.nativeViewProtected;
|
||||
if (this._containerViewId < 0) {
|
||||
this._containerViewId = android.view.View.generateViewId();
|
||||
}
|
||||
this._android.rootViewGroup.setId(this._containerViewId);
|
||||
}
|
||||
|
||||
@ -680,15 +698,16 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
|
||||
|
||||
if (!rootView) {
|
||||
navParam = application.getMainEntry();
|
||||
|
||||
if (navParam) {
|
||||
frame = new Frame();
|
||||
if (application.shouldCreateRootFrame()) {
|
||||
frame = rootView = new Frame();
|
||||
} else {
|
||||
rootView = loadViewFromEntry(navParam);
|
||||
}
|
||||
} else {
|
||||
// TODO: Throw an exception?
|
||||
throw new Error("A Frame must be used to navigate to a Page.");
|
||||
}
|
||||
|
||||
rootView = frame;
|
||||
}
|
||||
|
||||
// If there is savedInstanceState this call will recreate all fragments that were previously in the navigation.
|
||||
|
4
tns-core-modules/ui/frame/frame.d.ts
vendored
4
tns-core-modules/ui/frame/frame.d.ts
vendored
@ -421,6 +421,10 @@ export function reloadPage(): void;
|
||||
* @private
|
||||
*/
|
||||
export function resolvePageFromEntry(entry: NavigationEntry): Page;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export function loadViewFromEntry(entry: NavigationEntry): View;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
@ -20,42 +20,9 @@ const DELEGATE = "_delegate";
|
||||
|
||||
let navDepth = -1;
|
||||
|
||||
class NotificationObserver2 extends NSObject {
|
||||
private _onReceiveCallback: (notification: NSNotification) => void;
|
||||
|
||||
public static initWithCallback(onReceiveCallback: (notification: NSNotification) => void): NotificationObserver2 {
|
||||
const observer = <NotificationObserver2>super.new();
|
||||
observer._onReceiveCallback = onReceiveCallback;
|
||||
return observer;
|
||||
}
|
||||
|
||||
public onReceive(notification: NSNotification): void {
|
||||
this._onReceiveCallback(notification);
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
"onReceive": { returns: interop.types.void, params: [NSNotification] }
|
||||
};
|
||||
}
|
||||
|
||||
export const __observer = NotificationObserver2.initWithCallback(handleNotification);
|
||||
const notificationCenter = utils.ios.getter(NSNotificationCenter, NSNotificationCenter.defaultCenter);
|
||||
notificationCenter.addObserverSelectorNameObject(__observer, "onReceive", UIApplicationDidChangeStatusBarFrameNotification, null);
|
||||
|
||||
function handleNotification(notification: NSNotification): void {
|
||||
// When there is a 40px high "in-call" status bar, nobody moves the navigationBar top from 20 to 40 and it remains underneath the status bar.
|
||||
const frame = topmost() as Frame;
|
||||
if (frame) {
|
||||
frame._handleHigherInCallStatusBarIfNeeded();
|
||||
if (frame.currentPage) {
|
||||
frame.currentPage.requestLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Frame extends FrameBase {
|
||||
public viewController: UINavigationControllerImpl;
|
||||
private _ios: iOSFrame;
|
||||
private _paramToNavigate: any;
|
||||
public _animatedDelegate = <UINavigationControllerDelegate>UINavigationControllerAnimatedDelegate.new();
|
||||
|
||||
public _shouldSkipNativePop: boolean = false;
|
||||
@ -64,34 +31,14 @@ export class Frame extends FrameBase {
|
||||
public _heightMeasureSpec: number;
|
||||
public _right: number;
|
||||
public _bottom: number;
|
||||
public _isInitialNavigation: boolean = true;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._ios = new iOSFrame(this);
|
||||
this.viewController = this._ios.controller;
|
||||
this.nativeViewProtected = this._ios.controller.view;
|
||||
}
|
||||
|
||||
@profile
|
||||
public onLoaded() {
|
||||
super.onLoaded();
|
||||
|
||||
if (this._paramToNavigate) {
|
||||
this.navigate(this._paramToNavigate);
|
||||
this._paramToNavigate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public navigate(param: any) {
|
||||
if (this.isLoaded) {
|
||||
super.navigate(param);
|
||||
this._isInitialNavigation = false;
|
||||
}
|
||||
else {
|
||||
this._paramToNavigate = param;
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public _navigateCore(backstackEntry: BackstackEntry) {
|
||||
super._navigateCore(backstackEntry);
|
||||
@ -215,7 +162,6 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
|
||||
public _updateActionBar(page?: Page, disableNavBarAnimation: boolean = false): void {
|
||||
|
||||
super._updateActionBar(page);
|
||||
|
||||
if (page && this.currentPage && this.currentPage.modal === page) {
|
||||
@ -282,101 +228,12 @@ export class Frame extends FrameBase {
|
||||
FrameBase.defaultTransition = value;
|
||||
}
|
||||
|
||||
public requestLayout(): void {
|
||||
super.requestLayout();
|
||||
// Invalidate our Window so that layout is triggered again.
|
||||
let window = this.nativeViewProtected.window;
|
||||
if (window) {
|
||||
window.setNeedsLayout();
|
||||
}
|
||||
public layoutNativeView(left: number, top: number, right: number, bottom: number): void {
|
||||
//
|
||||
}
|
||||
|
||||
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
||||
let width = layout.getMeasureSpecSize(widthMeasureSpec);
|
||||
let widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
||||
|
||||
let height = layout.getMeasureSpecSize(heightMeasureSpec);
|
||||
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
||||
|
||||
this._widthMeasureSpec = widthMeasureSpec;
|
||||
this._heightMeasureSpec = heightMeasureSpec;
|
||||
|
||||
let result = this.measurePage(this.currentPage);
|
||||
if (this._navigateToEntry && this.currentPage) {
|
||||
let newPageSize = this.measurePage(this._navigateToEntry.resolvedPage);
|
||||
result.measuredWidth = Math.max(result.measuredWidth, newPageSize.measuredWidth);
|
||||
result.measuredHeight = Math.max(result.measuredHeight, newPageSize.measuredHeight);
|
||||
}
|
||||
|
||||
let widthAndState = View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0);
|
||||
let heightAndState = View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);
|
||||
|
||||
this.setMeasuredDimension(widthAndState, heightAndState);
|
||||
}
|
||||
|
||||
public measurePage(page: Page): { measuredWidth: number; measuredHeight: number } {
|
||||
// If background does not span under statusbar - reduce available height.
|
||||
let heightSpec: number = this._heightMeasureSpec;
|
||||
if (page && !page.backgroundSpanUnderStatusBar && !this.parent) {
|
||||
let height = layout.getMeasureSpecSize(this._heightMeasureSpec);
|
||||
let heightMode = layout.getMeasureSpecMode(this._heightMeasureSpec);
|
||||
let statusBarHeight = uiUtils.ios.getStatusBarHeight();
|
||||
heightSpec = layout.makeMeasureSpec(height - statusBarHeight, heightMode);
|
||||
}
|
||||
|
||||
return View.measureChild(this, page, this._widthMeasureSpec, heightSpec);
|
||||
}
|
||||
|
||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
||||
this._right = right;
|
||||
this._bottom = bottom;
|
||||
this._handleHigherInCallStatusBarIfNeeded();
|
||||
this.layoutPage(this.currentPage);
|
||||
if (this._navigateToEntry && this.currentPage) {
|
||||
this.layoutPage(this._navigateToEntry.resolvedPage);
|
||||
}
|
||||
}
|
||||
|
||||
public layoutPage(page: Page): void {
|
||||
if (page && (<any>page)._viewWillDisappear) {
|
||||
//https://github.com/NativeScript/NativeScript/issues/1201
|
||||
return;
|
||||
}
|
||||
|
||||
// If background does not span under statusbar - reduce available height and adjust top offset.
|
||||
let statusBarHeight = (page && !page.backgroundSpanUnderStatusBar && !this.parent) ? uiUtils.ios.getStatusBarHeight() : 0;
|
||||
|
||||
// Status bar height should be ignored when UINavigationBar is visible and not translucent
|
||||
if (this._ios.showNavigationBar &&
|
||||
!this._ios.controller.navigationBar.translucent &&
|
||||
page && (<any>page)._ios && !(<any>page)._ios.shown) {
|
||||
statusBarHeight = 0;
|
||||
}
|
||||
View.layoutChild(this, page, 0, statusBarHeight, this._right, this._bottom);
|
||||
}
|
||||
|
||||
public get navigationBarHeight(): number {
|
||||
let navigationBar = this._ios.controller.navigationBar;
|
||||
return (navigationBar && !this._ios.controller.navigationBarHidden) ? navigationBar.frame.size.height : 0;
|
||||
}
|
||||
|
||||
public _setNativeViewFrame(nativeView: any, frame: any) {
|
||||
// HACK: The plugin https://github.com/hackiftekhar/IQKeyboardManager offsets our Frame's 'nativeView.frame.origin.y'
|
||||
// to a negative value so the currently focused TextField/TextView is always on the screen while the soft keyboard is showing.
|
||||
// Our Frame always wants to have an origin of {0, 0}, so if someone else has been playing with origin.x or origin.y do not bring it back to {0, 0}.
|
||||
if (nativeView.frame.size.width === frame.size.width && nativeView.frame.size.height === frame.size.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
super._setNativeViewFrame(nativeView, frame);
|
||||
}
|
||||
|
||||
public remeasureFrame(): void {
|
||||
this.requestLayout();
|
||||
let window: UIWindow = this.nativeViewProtected.window;
|
||||
if (window) {
|
||||
window.layoutIfNeeded();
|
||||
}
|
||||
public _setNativeViewFrame(nativeView: UIView, frame: CGRect) {
|
||||
//
|
||||
}
|
||||
|
||||
public _onNavigatingTo(backstackEntry: BackstackEntry, isBack: boolean) {
|
||||
@ -397,7 +254,6 @@ export class Frame extends FrameBase {
|
||||
traceWrite(`Forcing navigationBar.frame.origin.y to ${statusBarHeight} due to a higher in-call status-bar`, traceCategories.Layout);
|
||||
}
|
||||
|
||||
this._ios.controller.navigationBar.autoresizingMask = UIViewAutoresizing.None;
|
||||
this._ios.controller.navigationBar.removeConstraints((<any>this)._ios.controller.navigationBar.constraints);
|
||||
this._ios.controller.navigationBar.frame = CGRectMake(
|
||||
this._ios.controller.navigationBar.frame.origin.x,
|
||||
@ -506,24 +362,12 @@ class UINavigationControllerImpl extends UINavigationController {
|
||||
@profile
|
||||
public viewWillAppear(animated: boolean): void {
|
||||
super.viewWillAppear(animated);
|
||||
let owner = this._owner.get();
|
||||
const owner = this._owner.get();
|
||||
if (owner && (!owner.isLoaded && !owner.parent)) {
|
||||
owner.onLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewDidLayoutSubviews(): void {
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(this._owner + " viewDidLayoutSubviews, isLoaded = " + owner.isLoaded, traceCategories.ViewHierarchy);
|
||||
}
|
||||
|
||||
owner._updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
private animateWithDuration(navigationTransition: NavigationTransition,
|
||||
nativeTransition: UIViewAnimationTransition,
|
||||
transitionType: string,
|
||||
@ -724,7 +568,6 @@ class iOSFrame implements iOSFrameDefinition {
|
||||
constructor(frame: Frame) {
|
||||
this._frame = frame;
|
||||
this._controller = UINavigationControllerImpl.initWithOwner(new WeakRef(frame));
|
||||
this._controller.automaticallyAdjustsScrollViewInsets = false;
|
||||
}
|
||||
|
||||
public get controller() {
|
||||
@ -735,17 +578,13 @@ class iOSFrame implements iOSFrameDefinition {
|
||||
return this._showNavigationBar;
|
||||
}
|
||||
public set showNavigationBar(value: boolean) {
|
||||
let change = this._showNavigationBar !== value;
|
||||
this._showNavigationBar = value;
|
||||
|
||||
let animated = !this._frame._isInitialNavigation && !this._disableNavBarAnimation;
|
||||
const viewController = this._controller.viewControllers;
|
||||
const length = viewController ? viewController.count : 0;
|
||||
const animated = length > 0 && !this._disableNavBarAnimation;
|
||||
|
||||
this._controller.setNavigationBarHiddenAnimated(!value, animated);
|
||||
|
||||
let currentPage = this._controller.owner.currentPage;
|
||||
if (currentPage && change) {
|
||||
currentPage.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public get navBarVisibility(): "auto" | "never" | "always" {
|
||||
@ -754,4 +593,4 @@ class iOSFrame implements iOSFrameDefinition {
|
||||
public set navBarVisibility(value: "auto" | "never" | "always") {
|
||||
this._navBarVisibility = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
export * from "./image-common";
|
||||
|
||||
export class Image extends ImageBase {
|
||||
private _ios: UIImageView;
|
||||
nativeViewProtected: UIImageView;
|
||||
private _imageSourceAffectsLayout: boolean = true;
|
||||
private _templateImageWasCreated: boolean;
|
||||
|
||||
@ -14,29 +14,26 @@ export class Image extends ImageBase {
|
||||
super();
|
||||
|
||||
//TODO: Think of unified way of setting all the default values.
|
||||
this.nativeViewProtected = this._ios = UIImageView.new();
|
||||
this._ios.contentMode = UIViewContentMode.ScaleAspectFit;
|
||||
this._ios.userInteractionEnabled = true;
|
||||
const imageView = UIImageView.new();
|
||||
imageView.contentMode = UIViewContentMode.ScaleAspectFit;
|
||||
imageView.userInteractionEnabled = true;
|
||||
this.nativeViewProtected = imageView;
|
||||
this._setNativeClipToBounds();
|
||||
}
|
||||
|
||||
get ios(): UIImageView {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
private setTintColor(value: Color) {
|
||||
if (value && this._ios.image && !this._templateImageWasCreated) {
|
||||
this._ios.image = this._ios.image.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
|
||||
if (value && this.nativeViewProtected.image && !this._templateImageWasCreated) {
|
||||
this.nativeViewProtected.image = this.nativeViewProtected.image.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
|
||||
this._templateImageWasCreated = true;
|
||||
} else if (this._ios.image && this._templateImageWasCreated) {
|
||||
} else if (this.nativeViewProtected.image && this._templateImageWasCreated) {
|
||||
this._templateImageWasCreated = false;
|
||||
this._ios.image = this._ios.image.imageWithRenderingMode(UIImageRenderingMode.Automatic);
|
||||
this.nativeViewProtected.image = this.nativeViewProtected.image.imageWithRenderingMode(UIImageRenderingMode.Automatic);
|
||||
}
|
||||
this._ios.tintColor = value ? value.ios : null;
|
||||
this.nativeViewProtected.tintColor = value ? value.ios : null;
|
||||
}
|
||||
|
||||
public _setNativeImage(nativeImage: UIImage) {
|
||||
this.ios.image = nativeImage;
|
||||
this.nativeViewProtected.image = nativeImage;
|
||||
this._templateImageWasCreated = false;
|
||||
this.setTintColor(this.style.tintColor);
|
||||
|
||||
@ -47,32 +44,32 @@ export class Image extends ImageBase {
|
||||
|
||||
_setNativeClipToBounds() {
|
||||
// Always set clipsToBounds for images
|
||||
this._ios.clipsToBounds = true;
|
||||
this.nativeViewProtected.clipsToBounds = true;
|
||||
}
|
||||
|
||||
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
||||
// We don't call super because we measure native view with specific size.
|
||||
let width = layout.getMeasureSpecSize(widthMeasureSpec);
|
||||
let widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
||||
const width = layout.getMeasureSpecSize(widthMeasureSpec);
|
||||
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
||||
|
||||
let height = layout.getMeasureSpecSize(heightMeasureSpec);
|
||||
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
||||
const height = layout.getMeasureSpecSize(heightMeasureSpec);
|
||||
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
||||
|
||||
let nativeWidth = this.imageSource ? layout.toDevicePixels(this.imageSource.width) : 0;
|
||||
let nativeHeight = this.imageSource ? layout.toDevicePixels(this.imageSource.height) : 0;
|
||||
const nativeWidth = this.imageSource ? layout.toDevicePixels(this.imageSource.width) : 0;
|
||||
const nativeHeight = this.imageSource ? layout.toDevicePixels(this.imageSource.height) : 0;
|
||||
|
||||
let measureWidth = Math.max(nativeWidth, this.effectiveMinWidth);
|
||||
let measureHeight = Math.max(nativeHeight, this.effectiveMinHeight);
|
||||
|
||||
let finiteWidth: boolean = widthMode !== layout.UNSPECIFIED;
|
||||
let finiteHeight: boolean = heightMode !== layout.UNSPECIFIED;
|
||||
const finiteWidth: boolean = widthMode !== layout.UNSPECIFIED;
|
||||
const finiteHeight: boolean = heightMode !== layout.UNSPECIFIED;
|
||||
|
||||
this._imageSourceAffectsLayout = widthMode !== layout.EXACTLY || heightMode !== layout.EXACTLY;
|
||||
|
||||
if (nativeWidth !== 0 && nativeHeight !== 0 && (finiteWidth || finiteHeight)) {
|
||||
let scale = Image.computeScaleFactor(width, height, finiteWidth, finiteHeight, nativeWidth, nativeHeight, this.stretch);
|
||||
let resultW = Math.round(nativeWidth * scale.width);
|
||||
let resultH = Math.round(nativeHeight * scale.height);
|
||||
const scale = Image.computeScaleFactor(width, height, finiteWidth, finiteHeight, nativeWidth, nativeHeight, this.stretch);
|
||||
const resultW = Math.round(nativeWidth * scale.width);
|
||||
const resultH = Math.round(nativeHeight * scale.height);
|
||||
|
||||
measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
|
||||
measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
|
||||
@ -84,8 +81,8 @@ export class Image extends ImageBase {
|
||||
}
|
||||
}
|
||||
|
||||
let widthAndState = Image.resolveSizeAndState(measureWidth, width, widthMode, 0);
|
||||
let heightAndState = Image.resolveSizeAndState(measureHeight, height, heightMode, 0);
|
||||
const widthAndState = Image.resolveSizeAndState(measureWidth, width, widthMode, 0);
|
||||
const heightAndState = Image.resolveSizeAndState(measureHeight, height, heightMode, 0);
|
||||
|
||||
this.setMeasuredDimension(widthAndState, heightAndState);
|
||||
}
|
||||
@ -123,44 +120,35 @@ export class Image extends ImageBase {
|
||||
return { width: scaleW, height: scaleH };
|
||||
}
|
||||
|
||||
[stretchProperty.getDefault](): "aspectFit" {
|
||||
return "aspectFit";
|
||||
}
|
||||
[stretchProperty.setNative](value: "none" | "aspectFill" | "aspectFit" | "fill") {
|
||||
switch (value) {
|
||||
case "aspectFit":
|
||||
this._ios.contentMode = UIViewContentMode.ScaleAspectFit;
|
||||
this.nativeViewProtected.contentMode = UIViewContentMode.ScaleAspectFit;
|
||||
break;
|
||||
|
||||
case "aspectFill":
|
||||
this._ios.contentMode = UIViewContentMode.ScaleAspectFill;
|
||||
this.nativeViewProtected.contentMode = UIViewContentMode.ScaleAspectFill;
|
||||
break;
|
||||
|
||||
case "fill":
|
||||
this._ios.contentMode = UIViewContentMode.ScaleToFill;
|
||||
this.nativeViewProtected.contentMode = UIViewContentMode.ScaleToFill;
|
||||
break;
|
||||
|
||||
case "none":
|
||||
default:
|
||||
this._ios.contentMode = UIViewContentMode.TopLeft;
|
||||
this.nativeViewProtected.contentMode = UIViewContentMode.TopLeft;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[tintColorProperty.getDefault](): Color {
|
||||
return undefined;
|
||||
}
|
||||
[tintColorProperty.setNative](value: Color) {
|
||||
this.setTintColor(value);
|
||||
}
|
||||
|
||||
[imageSourceProperty.getDefault](): ImageSource {
|
||||
return undefined;
|
||||
}
|
||||
[imageSourceProperty.setNative](value: ImageSource) {
|
||||
this._setNativeImage(value ? value.ios : null);
|
||||
}
|
||||
|
||||
[srcProperty.getDefault](): any {
|
||||
return undefined;
|
||||
}
|
||||
[srcProperty.setNative](value: any) {
|
||||
this._createImageSourceFromSrc(value);
|
||||
}
|
||||
|
@ -154,22 +154,22 @@ export class GridLayout extends LayoutBase {
|
||||
/**
|
||||
* Represents the observable property backing the column property.
|
||||
*/
|
||||
export const columnProperty: Property<GridLayout, number>;
|
||||
export const columnProperty: Property<View, number>;
|
||||
|
||||
/**
|
||||
* Represents the observable property backing the columnSpan property.
|
||||
*/
|
||||
export const columnSpanProperty: Property<GridLayout, number>;
|
||||
export const columnSpanProperty: Property<View, number>;
|
||||
|
||||
/**
|
||||
* Represents the observable property backing the row property.
|
||||
*/
|
||||
export const rowProperty: Property<GridLayout, number>;
|
||||
export const rowProperty: Property<View, number>;
|
||||
|
||||
/**
|
||||
* Represents the observable property backing the rowSpan property.
|
||||
*/
|
||||
export const rowSpanProperty: Property<GridLayout, number>;
|
||||
export const rowSpanProperty: Property<View, number>;
|
||||
|
||||
export type GridUnitType = "pixel" | "star" | "auto";
|
||||
export namespace GridUnitType {
|
||||
|
@ -384,24 +384,18 @@ class MeasureHelper {
|
||||
}
|
||||
|
||||
public setInfinityWidth(value: boolean): void {
|
||||
if (this.infinityWidth !== value) {
|
||||
this.infinityWidth = value;
|
||||
|
||||
for (let i = 0, size = this.columns.length; i < size; i++) {
|
||||
let columnGroup: ItemGroup = this.columns[i];
|
||||
columnGroup.setIsLengthInfinity(value);
|
||||
}
|
||||
this.infinityWidth = value;
|
||||
for (let i = 0, size = this.columns.length; i < size; i++) {
|
||||
let columnGroup: ItemGroup = this.columns[i];
|
||||
columnGroup.setIsLengthInfinity(value);
|
||||
}
|
||||
}
|
||||
|
||||
public setInfinityHeight(value: boolean): void {
|
||||
if (this.infinityHeight !== value) {
|
||||
this.infinityHeight = value;
|
||||
|
||||
for (let i = 0, size = this.rows.length; i < size; i++) {
|
||||
let rowGroup: ItemGroup = this.rows[i];
|
||||
rowGroup.setIsLengthInfinity(value);
|
||||
}
|
||||
this.infinityHeight = value;
|
||||
for (let i = 0, size = this.rows.length; i < size; i++) {
|
||||
let rowGroup: ItemGroup = this.rows[i];
|
||||
rowGroup.setIsLengthInfinity(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +230,6 @@ export class ListView extends ListViewBase {
|
||||
super();
|
||||
this.nativeViewProtected = this._ios = UITableView.new();
|
||||
this._ios.registerClassForCellReuseIdentifier(ListViewCell.class(), this._defaultTemplate.key);
|
||||
this._ios.autoresizingMask = UIViewAutoresizing.None;
|
||||
this._ios.estimatedRowHeight = DEFAULT_HEIGHT;
|
||||
this._ios.rowHeight = UITableViewAutomaticDimension;
|
||||
this._ios.dataSource = this._dataSource = DataSource.initWithOwner(new WeakRef(this));
|
||||
@ -411,7 +410,11 @@ export class ListView extends ListViewBase {
|
||||
this._removeView(view.parent);
|
||||
}
|
||||
|
||||
// No need to request layout when we are removing cells.
|
||||
const preparing = this._preparingCell;
|
||||
this._preparingCell = true;
|
||||
view.parent._removeView(view);
|
||||
this._preparingCell = preparing;
|
||||
this._map.delete(cell);
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
|
||||
public _modal: PageBase;
|
||||
public _fragmentTag: string;
|
||||
public _frame: Frame;
|
||||
|
||||
public actionBarHidden: boolean;
|
||||
public enableSwipeBackNavigation: boolean;
|
||||
@ -181,8 +182,7 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
public _addChildFromBuilder(name: string, value: any) {
|
||||
if (value instanceof ActionBar) {
|
||||
this.actionBar = value;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
super._addChildFromBuilder(name, value);
|
||||
}
|
||||
}
|
||||
@ -208,7 +208,7 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
}
|
||||
|
||||
public _raiseShownModallyEvent() {
|
||||
let args: ShownModallyData = {
|
||||
const args: ShownModallyData = {
|
||||
eventName: PageBase.shownModallyEvent,
|
||||
object: this,
|
||||
context: this._modalContext,
|
||||
@ -218,7 +218,7 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
}
|
||||
|
||||
protected _raiseShowingModallyEvent() {
|
||||
let args: ShownModallyData = {
|
||||
const args: ShownModallyData = {
|
||||
eventName: PageBase.showingModallyEvent,
|
||||
object: this,
|
||||
context: this._modalContext,
|
||||
@ -233,7 +233,7 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
}
|
||||
|
||||
get _childrenCount(): number {
|
||||
return (this.content ? 1 : 0) + (this.actionBar ? 1 : 0);
|
||||
return (this.content ? 1 : 0) + (this._actionBar ? 1 : 0);
|
||||
}
|
||||
|
||||
_inheritStyleScope(styleScope: StyleScope): void {
|
||||
|
4
tns-core-modules/ui/page/page.d.ts
vendored
4
tns-core-modules/ui/page/page.d.ts
vendored
@ -224,6 +224,10 @@ export class Page extends ContentView {
|
||||
* @private
|
||||
*/
|
||||
public _fragmentTag: string;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public _frame: Frame;
|
||||
|
||||
/**
|
||||
* A method called before navigating to the page.
|
||||
|
@ -1,5 +1,10 @@
|
||||
import {
|
||||
PageBase, View, layout,
|
||||
// Definitions.
|
||||
import { Frame } from "../frame";
|
||||
|
||||
// Types.
|
||||
import { ios as iosView } from "../core/view";
|
||||
import {
|
||||
PageBase, View, ViewBase, layout,
|
||||
actionBarHiddenProperty, statusBarStyleProperty,
|
||||
traceEnabled, traceWrite, traceCategories, PercentLength, Color
|
||||
} from "./page-common";
|
||||
@ -19,16 +24,15 @@ const ENTRY = "_entry";
|
||||
const DELEGATE = "_delegate";
|
||||
|
||||
function isBackNavigationTo(page: Page, entry): boolean {
|
||||
let frame = page.frame;
|
||||
const frame = page.frame;
|
||||
if (!frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame.navigationQueueIsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
let navigationQueue = (<any>frame)._navigationQueue;
|
||||
} else {
|
||||
const navigationQueue = (<any>frame)._navigationQueue;
|
||||
for (let i = 0; i < navigationQueue.length; i++) {
|
||||
if (navigationQueue[i].entry === entry) {
|
||||
return navigationQueue[i].isBackNavigation;
|
||||
@ -62,166 +66,92 @@ class UIViewControllerImpl extends UIViewController {
|
||||
|
||||
public isBackstackSkipped: boolean;
|
||||
public isBackstackCleared: boolean;
|
||||
public shown: boolean;
|
||||
|
||||
public static initWithOwner(owner: WeakRef<Page>): UIViewControllerImpl {
|
||||
let controller = <UIViewControllerImpl>UIViewControllerImpl.new();
|
||||
const controller = <UIViewControllerImpl>UIViewControllerImpl.new();
|
||||
controller._owner = owner;
|
||||
controller.automaticallyAdjustsScrollViewInsets = false;
|
||||
controller.shown = false;
|
||||
return controller;
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewDidLayoutSubviews() {
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (traceEnabled()) {
|
||||
traceWrite(owner + " viewDidLayoutSubviews, isLoaded = " + owner.isLoaded, traceCategories.ViewHierarchy);
|
||||
}
|
||||
|
||||
if (!owner.isLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalParent = owner._modalParent;
|
||||
if (modalParent) {
|
||||
// if inside horizontally compact environment - fullScreen will be forced
|
||||
let isFullScreen = !owner._UIModalPresentationFormSheet ||
|
||||
(modalParent.nativeViewProtected.traitCollection.horizontalSizeClass === UIUserInterfaceSizeClass.Compact);
|
||||
|
||||
let frame = isFullScreen ? getter(UIScreen, UIScreen.mainScreen).bounds : this.view.frame;
|
||||
let size = frame.size;
|
||||
let width = layout.toDevicePixels(size.width);
|
||||
let height = layout.toDevicePixels(size.height);
|
||||
let mode: number = layout.EXACTLY;
|
||||
|
||||
let superViewRotationRadians;
|
||||
if (this.view.superview) {
|
||||
let transform = this.view.superview.transform;
|
||||
superViewRotationRadians = atan2f(transform.b, transform.a);
|
||||
}
|
||||
|
||||
let bottom = height;
|
||||
let statusBarHeight = uiUtils.ios.getStatusBarHeight();
|
||||
let statusBarVisible = !getter(UIApplication, UIApplication.sharedApplication).statusBarHidden;
|
||||
let backgroundSpanUnderStatusBar = owner.backgroundSpanUnderStatusBar;
|
||||
if (statusBarVisible && !backgroundSpanUnderStatusBar) {
|
||||
height -= statusBarHeight;
|
||||
}
|
||||
|
||||
let widthSpec = layout.makeMeasureSpec(width, mode);
|
||||
let heightSpec = layout.makeMeasureSpec(height, mode);
|
||||
|
||||
View.measureChild(modalParent, owner, widthSpec, heightSpec);
|
||||
let top = ((backgroundSpanUnderStatusBar && isFullScreen) || !isFullScreen) ? 0 : statusBarHeight;
|
||||
View.layoutChild(modalParent, owner, 0, top, width, bottom);
|
||||
|
||||
if (traceEnabled()) {
|
||||
traceWrite(owner + ", native frame = " + NSStringFromCGRect(this.view.frame), traceCategories.Layout);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!iosApp.window) {
|
||||
uiUtils.ios._layoutRootView(owner, getter(UIScreen, UIScreen.mainScreen).bounds);
|
||||
}
|
||||
owner._updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewWillAppear(animated: boolean): void {
|
||||
super.viewWillAppear(animated);
|
||||
this.shown = false;
|
||||
let page = this._owner.get();
|
||||
|
||||
if (traceEnabled) {
|
||||
traceWrite(page + " viewWillAppear", traceCategories.Navigation);
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
const owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
const frame = this.navigationController ? (<any>this.navigationController).owner : null;
|
||||
const newEntry = this[ENTRY];
|
||||
const modalParent = page._modalParent;
|
||||
const modalParent = owner._modalParent;
|
||||
|
||||
// Don't raise event if currentPage was showing modal page.
|
||||
if (!page._presentedViewController && newEntry && (!frame || frame.currentPage !== page)) {
|
||||
let isBack = isBackNavigationTo(page, newEntry);
|
||||
page.onNavigatingTo(newEntry.entry.context, isBack, newEntry.entry.bindingContext);
|
||||
if (!owner._presentedViewController && newEntry && (!frame || frame.currentPage !== owner)) {
|
||||
const isBack = isBackNavigationTo(owner, newEntry);
|
||||
owner.onNavigatingTo(newEntry.entry.context, isBack, newEntry.entry.bindingContext);
|
||||
}
|
||||
|
||||
page._enableLoadedEvents = true;
|
||||
owner._enableLoadedEvents = true;
|
||||
|
||||
// Add page to frame if showing modal page.
|
||||
// TODO: This needs refactoring.
|
||||
if (modalParent) {
|
||||
modalParent.frame._addView(page);
|
||||
modalParent._addView(owner);
|
||||
}
|
||||
|
||||
if (frame) {
|
||||
if (!page.parent) {
|
||||
if (!owner.parent) {
|
||||
if (!frame._currentEntry) {
|
||||
frame._currentEntry = newEntry;
|
||||
} else {
|
||||
frame._navigateToEntry = newEntry;
|
||||
}
|
||||
|
||||
frame._addView(page);
|
||||
frame.remeasureFrame();
|
||||
} else if (page.parent !== frame) {
|
||||
owner._frame = frame;
|
||||
frame._addView(owner);
|
||||
} else if (owner.parent !== frame) {
|
||||
throw new Error("Page is already shown on another frame.");
|
||||
}
|
||||
|
||||
page.actionBar.update();
|
||||
owner.actionBar.update();
|
||||
}
|
||||
|
||||
//https://github.com/NativeScript/NativeScript/issues/1201
|
||||
page._viewWillDisappear = false;
|
||||
owner._viewWillDisappear = false;
|
||||
|
||||
// Pages in backstack are unloaded so raise loaded here.
|
||||
if (!page.isLoaded) {
|
||||
page.onLoaded();
|
||||
if (!owner.isLoaded) {
|
||||
owner.onLoaded();
|
||||
}
|
||||
|
||||
page._enableLoadedEvents = false;
|
||||
owner._enableLoadedEvents = false;
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewDidAppear(animated: boolean): void {
|
||||
super.viewDidAppear(animated);
|
||||
this.shown = true;
|
||||
let page = this._owner.get();
|
||||
if (traceEnabled()) {
|
||||
traceWrite(page + " viewDidAppear", traceCategories.Navigation);
|
||||
}
|
||||
if (!page) {
|
||||
|
||||
const owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
//https://github.com/NativeScript/NativeScript/issues/1201
|
||||
page._viewWillDisappear = false;
|
||||
owner._viewWillDisappear = false;
|
||||
|
||||
let frame = this.navigationController ? (<any>this.navigationController).owner : null;
|
||||
const frame = this.navigationController ? (<any>this.navigationController).owner : null;
|
||||
// Skip navigation events if modal page is shown.
|
||||
if (!page._presentedViewController && frame) {
|
||||
let newEntry = this[ENTRY];
|
||||
let isBack = isBackNavigationTo(page, newEntry);
|
||||
if (!owner._presentedViewController && frame) {
|
||||
const newEntry = this[ENTRY];
|
||||
let isBack = isBackNavigationTo(owner, newEntry);
|
||||
// We are on the current page which happens when navigation is canceled so isBack should be false.
|
||||
if (frame.currentPage === page && frame._navigationQueue.length === 0) {
|
||||
if (frame.currentPage === owner && frame._navigationQueue.length === 0) {
|
||||
isBack = false;
|
||||
}
|
||||
|
||||
frame._navigateToEntry = null;
|
||||
frame._currentEntry = newEntry;
|
||||
frame.remeasureFrame();
|
||||
frame._updateActionBar(page);
|
||||
|
||||
page.onNavigatedTo(isBack);
|
||||
owner.onNavigatedTo(isBack);
|
||||
|
||||
// If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.
|
||||
frame.ios.controller.delegate = this[DELEGATE];
|
||||
@ -229,49 +159,46 @@ class UIViewControllerImpl extends UIViewController {
|
||||
// Workaround for disabled backswipe on second custom native transition
|
||||
if (frame.canGoBack()) {
|
||||
this.navigationController.interactivePopGestureRecognizer.delegate = this.navigationController;
|
||||
this.navigationController.interactivePopGestureRecognizer.enabled = page.enableSwipeBackNavigation;
|
||||
this.navigationController.interactivePopGestureRecognizer.enabled = owner.enableSwipeBackNavigation;
|
||||
} else {
|
||||
this.navigationController.interactivePopGestureRecognizer.enabled = false;
|
||||
}
|
||||
|
||||
frame._processNavigationQueue(page);
|
||||
frame._processNavigationQueue(owner);
|
||||
}
|
||||
|
||||
if (!this.presentedViewController) {
|
||||
// clear presented viewController here only if no presented controller.
|
||||
// this is needed because in iOS9 the order of events could be - willAppear, willDisappear, didAppear.
|
||||
// If we clean it when we have viewController then once presented VC is dismissed then
|
||||
page._presentedViewController = null;
|
||||
owner._presentedViewController = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewWillDisappear(animated: boolean): void {
|
||||
super.viewWillDisappear(animated);
|
||||
|
||||
let page = this._owner.get();
|
||||
if (traceEnabled()) {
|
||||
traceWrite(page + " viewWillDisappear", traceCategories.Navigation);
|
||||
}
|
||||
if (!page) {
|
||||
const owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache presentedViewController if any. We don't want to raise
|
||||
// navigation events in case of presenting view controller.
|
||||
if (!page._presentedViewController) {
|
||||
page._presentedViewController = this.presentedViewController;
|
||||
if (!owner._presentedViewController) {
|
||||
owner._presentedViewController = this.presentedViewController;
|
||||
}
|
||||
|
||||
const frame = page.frame;
|
||||
const frame = owner.frame;
|
||||
// Skip navigation events if we are hiding because we are about to show modal page.
|
||||
if (!page._presentedViewController && frame && frame.currentPage === page) {
|
||||
let isBack = isBackNavigationFrom(this, page);
|
||||
page.onNavigatingFrom(isBack);
|
||||
if (!owner._presentedViewController && frame && frame.currentPage === owner) {
|
||||
let isBack = isBackNavigationFrom(this, owner);
|
||||
owner.onNavigatingFrom(isBack);
|
||||
}
|
||||
|
||||
//https://github.com/NativeScript/NativeScript/issues/1201
|
||||
page._viewWillDisappear = true;
|
||||
owner._viewWillDisappear = true;
|
||||
}
|
||||
|
||||
@profile
|
||||
@ -279,9 +206,6 @@ class UIViewControllerImpl extends UIViewController {
|
||||
super.viewDidDisappear(animated);
|
||||
|
||||
const page = this._owner.get();
|
||||
if (traceEnabled()) {
|
||||
traceWrite(page + " viewDidDisappear", traceCategories.Navigation);
|
||||
}
|
||||
// Exit if no page or page is hiding because it shows another page modally.
|
||||
if (!page || page.modal || page._presentedViewController) {
|
||||
return;
|
||||
@ -293,13 +217,13 @@ class UIViewControllerImpl extends UIViewController {
|
||||
|
||||
// Clear up after modal page has closed.
|
||||
if (modalParent) {
|
||||
modalParent.frame._removeView(page);
|
||||
modalParent._removeView(page);
|
||||
modalParent._modal = undefined;
|
||||
}
|
||||
|
||||
// Manually pop backStack when Back button is pressed or navigating back with edge swipe.
|
||||
// Don't pop if we are hiding modally shown page.
|
||||
let frame = page.frame;
|
||||
const frame = page.frame;
|
||||
// We are not modal page, have frame with backstack and navigation queue is empty and currentPage is closed
|
||||
// then pop our backstack.
|
||||
if (!modalParent && frame && frame.backStack.length > 0 && frame.navigationQueueIsEmpty() && frame.currentPage === page) {
|
||||
@ -310,10 +234,11 @@ class UIViewControllerImpl extends UIViewController {
|
||||
|
||||
// Remove from parent if page was in frame and we navigated back.
|
||||
// Showing page modally will not pass isBack check so currentPage won't be removed from Frame.
|
||||
let isBack = isBackNavigationFrom(this, page);
|
||||
const isBack = isBackNavigationFrom(this, page);
|
||||
if (isBack) {
|
||||
// Remove parent when navigating back.
|
||||
frame._removeView(page);
|
||||
page._frame = null;
|
||||
}
|
||||
|
||||
// Forward navigation does not remove page from frame so we raise unloaded manually.
|
||||
@ -328,9 +253,18 @@ class UIViewControllerImpl extends UIViewController {
|
||||
page.onNavigatedFrom(isBack);
|
||||
}
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews(): void {
|
||||
super.viewDidLayoutSubviews();
|
||||
|
||||
const owner = this._owner.get();
|
||||
iosView.layoutView(this, owner);
|
||||
}
|
||||
}
|
||||
|
||||
const whiteColor = new Color("white").ios;
|
||||
export class Page extends PageBase {
|
||||
public viewController: UIViewControllerImpl;
|
||||
nativeViewProtected: UIView;
|
||||
|
||||
private _ios: UIViewControllerImpl;
|
||||
@ -342,16 +276,26 @@ export class Page extends PageBase {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._ios = UIViewControllerImpl.initWithOwner(new WeakRef(this));
|
||||
this.nativeViewProtected = this._ios.view;
|
||||
this.nativeViewProtected.backgroundColor = new Color("white").ios;
|
||||
const controller = UIViewControllerImpl.initWithOwner(new WeakRef(this));
|
||||
this.viewController = this._ios = controller;
|
||||
this.nativeViewProtected = controller.view;
|
||||
this.nativeViewProtected.backgroundColor = whiteColor;
|
||||
}
|
||||
|
||||
public requestLayout(): void {
|
||||
super.requestLayout();
|
||||
if ((!this.parent || this._modalParent) && this.ios && this.nativeViewProtected) {
|
||||
this.nativeViewProtected.setNeedsLayout();
|
||||
}
|
||||
get ios(): UIViewController {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
get frame(): Frame {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
public layoutNativeView(left: number, top: number, right: number, bottom: number): void {
|
||||
//
|
||||
}
|
||||
|
||||
public _setNativeViewFrame(nativeView: UIView, frame: CGRect) {
|
||||
//
|
||||
}
|
||||
|
||||
public _onContentChanged(oldView: View, newView: View) {
|
||||
@ -384,9 +328,12 @@ export class Page extends PageBase {
|
||||
}
|
||||
if (view.ios instanceof UIView) {
|
||||
this._ios.view.addSubview(view.ios);
|
||||
} else if (view.ios instanceof UIViewController) {
|
||||
this._ios.addChildViewController(view.ios);
|
||||
this._ios.view.addSubview(view.ios.view);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -397,18 +344,17 @@ export class Page extends PageBase {
|
||||
traceWrite("Native: Removing " + view + " from " + this, traceCategories.ViewHierarchy);
|
||||
}
|
||||
if (view.ios instanceof UIView) {
|
||||
(<UIView>view.ios).removeFromSuperview();
|
||||
} else if (view.ios instanceof UIViewController) {
|
||||
(<UIViewController>view.ios).removeFromParentViewController();
|
||||
(<UIViewController>view.ios).view.removeFromSuperview();
|
||||
view.ios.removeFromSuperview();
|
||||
} else {
|
||||
const viewController = view.ios instanceof UIViewController ? view.ios : view.viewController;
|
||||
if (viewController) {
|
||||
view.ios.removeFromParentViewController();
|
||||
view.ios.view.removeFromSuperview();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get ios(): UIViewController {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
protected _showNativeModalView(parent: Page, context: any, closeCallback: Function, fullscreen?: boolean) {
|
||||
super._showNativeModalView(parent, context, closeCallback, fullscreen);
|
||||
this._modalParent = parent;
|
||||
@ -419,20 +365,18 @@ export class Page extends PageBase {
|
||||
|
||||
if (fullscreen) {
|
||||
this._ios.modalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this._ios.modalPresentationStyle = UIModalPresentationStyle.FormSheet;
|
||||
this._UIModalPresentationFormSheet = true;
|
||||
}
|
||||
|
||||
super._raiseShowingModallyEvent();
|
||||
this._raiseShowingModallyEvent();
|
||||
|
||||
parent.ios.presentViewControllerAnimatedCompletion(this._ios, true, null);
|
||||
let transitionCoordinator = getter(parent.ios, parent.ios.transitionCoordinator);
|
||||
const transitionCoordinator = getter(parent.ios, parent.ios.transitionCoordinator);
|
||||
if (transitionCoordinator) {
|
||||
UIViewControllerTransitionCoordinator.prototype.animateAlongsideTransitionCompletion.call(transitionCoordinator, null, () => this._raiseShownModallyEvent());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Apparently iOS 9+ stops all transitions and animations upon application suspend and transitionCoordinator becomes null here in this case.
|
||||
// Since we are not waiting for any transition to complete, i.e. transitionCoordinator is null, we can directly raise our shownModally event.
|
||||
// Take a look at https://github.com/NativeScript/NativeScript/issues/2173 for more info and a sample project.
|
||||
@ -461,10 +405,10 @@ export class Page extends PageBase {
|
||||
public _updateStatusBarStyle(value?: string) {
|
||||
const frame = this.frame;
|
||||
if (this.frame && value) {
|
||||
let navigationController = frame.ios.controller;
|
||||
let navigationBar = navigationController.navigationBar;
|
||||
const navigationController: UINavigationController = frame.ios.controller;
|
||||
const navigationBar = navigationController.navigationBar;
|
||||
|
||||
navigationBar.barStyle = value === "dark" ? 1 : 0;
|
||||
navigationBar.barStyle = value === "dark" ? UIBarStyle.Black : UIBarStyle.Default;
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,10 +428,10 @@ export class Page extends PageBase {
|
||||
if (!this.backgroundSpanUnderStatusBar) {
|
||||
const style = this.style;
|
||||
|
||||
let parentHeightMeasureSpec = parent._currentHeightMeasureSpec;
|
||||
let parentHeightMeasureSize = layout.getMeasureSpecSize(parentHeightMeasureSpec) - uiUtils.ios.getStatusBarHeight();
|
||||
let parentHeightMeasureMode = layout.getMeasureSpecMode(parentHeightMeasureSpec);
|
||||
let parentAvailableHeight = parentHeightMeasureMode === layout.UNSPECIFIED ? -1 : parentHeightMeasureSize;
|
||||
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);
|
||||
@ -495,78 +439,33 @@ export class Page extends PageBase {
|
||||
}
|
||||
|
||||
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) {
|
||||
let width = layout.getMeasureSpecSize(widthMeasureSpec);
|
||||
let widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
||||
const width = layout.getMeasureSpecSize(widthMeasureSpec);
|
||||
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
||||
|
||||
let height = layout.getMeasureSpecSize(heightMeasureSpec);
|
||||
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
||||
|
||||
let actionBarWidth: 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;
|
||||
|
||||
// If this page is inside nested frame - don't substract statusBarHeight again.
|
||||
if (this.frame && this.frame.parent) {
|
||||
statusBarHeight = 0;
|
||||
}
|
||||
|
||||
// Phones does not support fullScreen=false for modal pages so we reduce statusbar only when on tablet and not in fullscreen
|
||||
if (this._modalParent && this._UIModalPresentationFormSheet && device.deviceType === "Tablet") {
|
||||
statusBarHeight = 0;
|
||||
}
|
||||
const height = layout.getMeasureSpecSize(heightMeasureSpec);
|
||||
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
||||
|
||||
if (!this._modalParent && this.frame && this.frame._getNavBarVisible(this)) {
|
||||
// Measure ActionBar with the full height.
|
||||
let actionBarSize = View.measureChild(this, this.actionBar, widthMeasureSpec, layout.makeMeasureSpec(height, layout.AT_MOST));
|
||||
actionBarWidth = actionBarSize.measuredWidth;
|
||||
actionBarHeight = actionBarSize.measuredHeight;
|
||||
View.measureChild(this, this.actionBar, widthMeasureSpec, layout.makeMeasureSpec(height, layout.AT_MOST));
|
||||
}
|
||||
|
||||
let heightSpec = layout.makeMeasureSpec(height - actionBarHeight - statusBarHeight, heightMode);
|
||||
const heightSpec = layout.makeMeasureSpec(height, heightMode);
|
||||
|
||||
// Measure content with height - navigationBarHeight. Here we could use actionBarSize.measuredHeight probably.
|
||||
let result = View.measureChild(this, this.layoutView, widthMeasureSpec, heightSpec);
|
||||
const result = View.measureChild(this, this.layoutView, widthMeasureSpec, heightSpec);
|
||||
|
||||
let measureWidth = Math.max(actionBarWidth, result.measuredWidth, this.effectiveMinWidth);
|
||||
let measureHeight = Math.max(result.measuredHeight + actionBarHeight, this.effectiveMinHeight);
|
||||
const measureWidth = Math.max(result.measuredWidth, this.effectiveMinWidth);
|
||||
const measureHeight = Math.max(result.measuredHeight, this.effectiveMinHeight);
|
||||
|
||||
let widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
|
||||
let heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
|
||||
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
|
||||
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
|
||||
|
||||
this.setMeasuredDimension(widthAndState, heightAndState);
|
||||
}
|
||||
|
||||
public onLayout(left: number, top: number, right: number, bottom: number) {
|
||||
View.layoutChild(this, this.actionBar, 0, 0, right - left, bottom - top);
|
||||
|
||||
let navigationBarHeight: number = 0;
|
||||
if (this.frame && this.frame._getNavBarVisible(this)) {
|
||||
navigationBarHeight = this.actionBar.getMeasuredHeight();
|
||||
}
|
||||
|
||||
// Navigation bar height should be ignored when it is visible and not translucent
|
||||
if (this.frame && this.frame.ios &&
|
||||
this.frame.ios.controller.navigationBar &&
|
||||
!this.frame.ios.controller.navigationBar.translucent &&
|
||||
!this._ios.shown) {
|
||||
navigationBarHeight = 0;
|
||||
}
|
||||
|
||||
let statusBarHeight = this.backgroundSpanUnderStatusBar ? uiUtils.ios.getStatusBarHeight() : 0;
|
||||
|
||||
// If this page is inside nested frame - don't substract statusBarHeight again.
|
||||
if (this.frame && this.frame.parent) {
|
||||
statusBarHeight = 0;
|
||||
}
|
||||
|
||||
// Phones does not support fullScreen=false for modal pages so we reduce statusbar only when on tablet and not in fullscreen
|
||||
if (this._modalParent && this._UIModalPresentationFormSheet && device.deviceType === "Tablet") {
|
||||
statusBarHeight = 0;
|
||||
}
|
||||
|
||||
View.layoutChild(this, this.layoutView, 0, navigationBarHeight + statusBarHeight, right - left, bottom - top);
|
||||
View.layoutChild(this, this.layoutView, 0, top, right - left, bottom);
|
||||
}
|
||||
|
||||
public _addViewToNativeVisualTree(view: View): boolean {
|
||||
@ -587,9 +486,6 @@ export class Page extends PageBase {
|
||||
super._removeViewFromNativeVisualTree(view);
|
||||
}
|
||||
|
||||
[actionBarHiddenProperty.getDefault](): boolean {
|
||||
return undefined;
|
||||
}
|
||||
[actionBarHiddenProperty.setNative](value: boolean) {
|
||||
this._updateEnableSwipeBackNavigation(value);
|
||||
if (this.isLoaded) {
|
||||
@ -602,9 +498,9 @@ export class Page extends PageBase {
|
||||
return UIBarStyle.Default;
|
||||
}
|
||||
[statusBarStyleProperty.setNative](value: string | UIBarStyle) {
|
||||
let frame = this.frame;
|
||||
const frame = this.frame;
|
||||
if (frame) {
|
||||
let navigationBar = (<UINavigationController>frame.ios.controller).navigationBar;
|
||||
const navigationBar = (<UINavigationController>frame.ios.controller).navigationBar;
|
||||
if (typeof value === "string") {
|
||||
navigationBar.barStyle = value === "dark" ? UIBarStyle.Black : UIBarStyle.Default;
|
||||
} else {
|
||||
|
@ -92,10 +92,6 @@ export class ScrollView extends ScrollViewBase {
|
||||
this.updateScrollBarVisibility(value);
|
||||
}
|
||||
|
||||
get ios(): UIView {
|
||||
return this.nativeViewProtected;
|
||||
}
|
||||
|
||||
public scrollToVerticalOffset(value: number, animated: boolean) {
|
||||
if (this.orientation === "vertical") {
|
||||
const bounds = this.nativeViewProtected.bounds.size;
|
||||
@ -118,27 +114,23 @@ export class ScrollView extends ScrollViewBase {
|
||||
const height = layout.getMeasureSpecSize(heightMeasureSpec);
|
||||
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
||||
|
||||
const density = layout.getDisplayDensity();
|
||||
const child = this.layoutView;
|
||||
if (!child) {
|
||||
this._contentMeasuredWidth = this.effectiveMinWidth * density;
|
||||
this._contentMeasuredHeight = this.effectiveMinHeight * density;
|
||||
}
|
||||
else {
|
||||
this._contentMeasuredWidth = this.effectiveMinWidth;
|
||||
this._contentMeasuredHeight = this.effectiveMinHeight;
|
||||
if (child) {
|
||||
let childSize: { measuredWidth: number; measuredHeight: number };
|
||||
if (this.orientation === "vertical") {
|
||||
childSize = View.measureChild(this, child, widthMeasureSpec, layout.makeMeasureSpec(0, layout.UNSPECIFIED));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
childSize = View.measureChild(this, child, layout.makeMeasureSpec(0, layout.UNSPECIFIED), heightMeasureSpec);
|
||||
}
|
||||
|
||||
let w = layout.toDeviceIndependentPixels(childSize.measuredWidth);
|
||||
let h = layout.toDeviceIndependentPixels(childSize.measuredHeight);
|
||||
const w = layout.toDeviceIndependentPixels(childSize.measuredWidth);
|
||||
const h = layout.toDeviceIndependentPixels(childSize.measuredHeight);
|
||||
this.nativeViewProtected.contentSize = CGSizeMake(w, h);
|
||||
|
||||
this._contentMeasuredWidth = Math.max(childSize.measuredWidth, this.effectiveMinWidth * density);
|
||||
this._contentMeasuredHeight = Math.max(childSize.measuredHeight, this.effectiveMinHeight * density);
|
||||
this._contentMeasuredWidth = Math.max(childSize.measuredWidth, this.effectiveMinWidth);
|
||||
this._contentMeasuredHeight = Math.max(childSize.measuredHeight, this.effectiveMinHeight);
|
||||
}
|
||||
|
||||
const widthAndState = View.resolveSizeAndState(this._contentMeasuredWidth, width, widthMode, 0);
|
||||
@ -148,14 +140,12 @@ export class ScrollView extends ScrollViewBase {
|
||||
}
|
||||
|
||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
||||
|
||||
const width = (right - left);
|
||||
const height = (bottom - top);
|
||||
|
||||
if (this.orientation === "horizontal") {
|
||||
View.layoutChild(this, this.layoutView, 0, 0, Math.max(this._contentMeasuredWidth, width), height);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
View.layoutChild(this, this.layoutView, 0, 0, width, Math.max(this._contentMeasuredHeight, height));
|
||||
}
|
||||
}
|
||||
|
@ -57,11 +57,7 @@ function initializeNativeClasses() {
|
||||
traceWrite("TabView.PagerAdapter.instantiateItem; container: " + container + "; index: " + index, traceCategory);
|
||||
}
|
||||
|
||||
let item = this.items[index];
|
||||
// if (item.view.parent !== this.owner) {
|
||||
// this.owner._addView(item.view);
|
||||
// }
|
||||
|
||||
const item = this.items[index];
|
||||
if (this[VIEWS_STATES]) {
|
||||
if (traceEnabled()) {
|
||||
traceWrite("TabView.PagerAdapter.instantiateItem; restoreHierarchyState: " + item.view, traceCategory);
|
||||
@ -72,6 +68,7 @@ function initializeNativeClasses() {
|
||||
if (item.view.nativeViewProtected) {
|
||||
container.addView(item.view.nativeViewProtected);
|
||||
}
|
||||
|
||||
return item.view.nativeViewProtected;
|
||||
}
|
||||
|
||||
@ -103,17 +100,19 @@ function initializeNativeClasses() {
|
||||
}
|
||||
|
||||
const owner: TabView = this.owner;
|
||||
if (owner._childrenCount === 0) {
|
||||
// no owner could happen when tabView was shown as modal.
|
||||
if (!owner || owner._childrenCount === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this[VIEWS_STATES]) {
|
||||
this[VIEWS_STATES] = new android.util.SparseArray<android.os.Parcelable>();
|
||||
}
|
||||
let viewStates = this[VIEWS_STATES];
|
||||
let childCallback = function (view: View): boolean {
|
||||
let nativeView: android.view.View = view.nativeViewProtected;
|
||||
if (nativeView && nativeView.isSaveFromParentEnabled && nativeView.isSaveFromParentEnabled()) {
|
||||
|
||||
const viewStates = this[VIEWS_STATES];
|
||||
const childCallback = function (view: View): boolean {
|
||||
const nativeView: android.view.View = view.nativeViewProtected;
|
||||
if (nativeView && nativeView.isSaveFromParentEnabled()) {
|
||||
nativeView.saveHierarchyState(viewStates);
|
||||
}
|
||||
return true;
|
||||
@ -121,7 +120,7 @@ function initializeNativeClasses() {
|
||||
|
||||
owner.eachChildView(childCallback);
|
||||
|
||||
let bundle = new android.os.Bundle();
|
||||
const bundle = new android.os.Bundle();
|
||||
bundle.putSparseParcelableArray(VIEWS_STATES, viewStates);
|
||||
return bundle;
|
||||
}
|
||||
@ -351,6 +350,18 @@ export class TabView extends TabViewBase {
|
||||
super.disposeNativeView();
|
||||
}
|
||||
|
||||
public _loadEachChild(): void {
|
||||
// We load childs once they are added to native parent or Frame
|
||||
// cannot navigate because its nativeView is not added to window.
|
||||
const index = this.selectedIndex;
|
||||
if (index >= 0) {
|
||||
const item = this.items[index];
|
||||
if (!item.isLoaded && this.isLoaded) {
|
||||
item.onLoaded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setAdapterItems(items: Array<TabViewItem>) {
|
||||
(<any>this._pagerAdapter).items = items;
|
||||
|
||||
@ -401,7 +412,12 @@ export class TabView extends TabViewBase {
|
||||
if (traceEnabled()) {
|
||||
traceWrite("TabView this._viewPager.setCurrentItem(" + value + ", true);", traceCategory);
|
||||
}
|
||||
|
||||
const item = this.items[value];
|
||||
this._viewPager.setCurrentItem(value, true);
|
||||
if (!item.isLoaded && this.isLoaded) {
|
||||
item.onLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
[itemsProperty.getDefault](): TabViewItem[] {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Font } from "../styling/font";
|
||||
|
||||
import { ios as iosView } from "../core/view";
|
||||
import {
|
||||
TabViewBase, TabViewItemBase, itemsProperty, selectedIndexProperty,
|
||||
tabTextColorProperty, tabBackgroundColorProperty, selectedTabTextColorProperty, iosIconRenderingModeProperty,
|
||||
@ -9,9 +10,13 @@ import { textTransformProperty, TextTransform, getTransformedText } from "../tex
|
||||
import { fromFileOrResource } from "../../image-source";
|
||||
import { Page } from "../page";
|
||||
import { profile } from "../../profiling";
|
||||
import * as uiUtils from "../utils";
|
||||
import * as utils from "../../utils/utils";
|
||||
|
||||
export * from "./tab-view-common";
|
||||
|
||||
const getter = utils.ios.getter;
|
||||
|
||||
class UITabBarControllerImpl extends UITabBarController {
|
||||
|
||||
private _owner: WeakRef<TabView>;
|
||||
@ -22,14 +27,11 @@ class UITabBarControllerImpl extends UITabBarController {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews(): void {
|
||||
if (traceEnabled()) {
|
||||
traceWrite("TabView.UITabBarControllerClass.viewDidLayoutSubviews();", traceCategories.Debug);
|
||||
}
|
||||
super.viewDidLayoutSubviews();
|
||||
let owner = this._owner.get();
|
||||
if (owner && owner.isLoaded) {
|
||||
owner._updateLayout();
|
||||
public viewWillAppear(animated: boolean): void {
|
||||
super.viewWillAppear(animated);
|
||||
const owner = this._owner.get();
|
||||
if (owner && !owner.isLoaded && !owner.parent) {
|
||||
owner.onLoaded();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +67,7 @@ class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControl
|
||||
traceWrite("TabView.delegate.DID_select(" + tabBarController + ", " + viewController + ");", traceCategories.Debug);
|
||||
}
|
||||
|
||||
let owner = this._owner.get();
|
||||
const owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onViewControllerShown(viewController);
|
||||
}
|
||||
@ -113,8 +115,7 @@ class UINavigationControllerDelegateImpl extends NSObject implements UINavigatio
|
||||
function updateItemTitlePosition(tabBarItem: UITabBarItem): void {
|
||||
if (typeof (<any>tabBarItem).setTitlePositionAdjustment === "function") {
|
||||
(<any>tabBarItem).setTitlePositionAdjustment({ horizontal: 0, vertical: -20 });
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tabBarItem.titlePositionAdjustment = { horizontal: 0, vertical: -20 };
|
||||
}
|
||||
}
|
||||
@ -124,21 +125,20 @@ function updateItemIconPosition(tabBarItem: UITabBarItem): void {
|
||||
}
|
||||
|
||||
export class TabViewItem extends TabViewItemBase {
|
||||
private _iosViewController: UIViewController;
|
||||
|
||||
private __controller: UIViewController;
|
||||
public setViewController(controller: UIViewController) {
|
||||
this._iosViewController = controller;
|
||||
this.setNativeView((<any>this)._nativeView = controller.view);
|
||||
this.__controller = controller;
|
||||
this.setNativeView(controller.view);
|
||||
}
|
||||
|
||||
public disposeNativeView() {
|
||||
this._iosViewController = undefined;
|
||||
this.__controller = undefined;
|
||||
this.setNativeView(undefined);
|
||||
}
|
||||
|
||||
public _update() {
|
||||
const parent = <TabView>this.parent;
|
||||
let controller = this._iosViewController;
|
||||
const controller = this.__controller;
|
||||
if (parent && controller) {
|
||||
const icon = parent._getIcon(this.iconSource);
|
||||
const index = parent.items.indexOf(this);
|
||||
@ -147,8 +147,7 @@ export class TabViewItem extends TabViewItemBase {
|
||||
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(title, icon, index);
|
||||
if (!icon) {
|
||||
updateItemTitlePosition(tabBarItem);
|
||||
}
|
||||
else if (!title) {
|
||||
} else if (!title) {
|
||||
updateItemIconPosition(tabBarItem);
|
||||
}
|
||||
|
||||
@ -166,6 +165,7 @@ export class TabViewItem extends TabViewItemBase {
|
||||
}
|
||||
|
||||
export class TabView extends TabViewBase {
|
||||
public viewController: UITabBarControllerImpl;
|
||||
public _ios: UITabBarControllerImpl;
|
||||
private _delegate: UITabBarControllerDelegateImpl;
|
||||
private _moreNavigationControllerDelegate: UINavigationControllerDelegateImpl;
|
||||
@ -176,7 +176,7 @@ export class TabView extends TabViewBase {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._ios = UITabBarControllerImpl.initWithOwner(new WeakRef(this));
|
||||
this.viewController = this._ios = UITabBarControllerImpl.initWithOwner(new WeakRef(this));
|
||||
this.nativeViewProtected = this._ios.view;
|
||||
this._delegate = UITabBarControllerDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
this._moreNavigationControllerDelegate = UINavigationControllerDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
@ -199,9 +199,13 @@ export class TabView extends TabViewBase {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
// get nativeView(): UIView {
|
||||
// return this._ios.view;
|
||||
// }
|
||||
public layoutNativeView(left: number, top: number, right: number, bottom: number): void {
|
||||
//
|
||||
}
|
||||
|
||||
public _setNativeViewFrame(nativeView: UIView, frame: CGRect) {
|
||||
//
|
||||
}
|
||||
|
||||
public _onViewControllerShown(viewController: UIViewController) {
|
||||
// This method could be called with the moreNavigationController or its list controller, so we have to check.
|
||||
@ -210,8 +214,7 @@ export class TabView extends TabViewBase {
|
||||
}
|
||||
if (this._ios.viewControllers && this._ios.viewControllers.containsObject(viewController)) {
|
||||
this.selectedIndex = this._ios.viewControllers.indexOfObject(viewController);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (traceEnabled()) {
|
||||
traceWrite("TabView._onViewControllerShown: viewController is not one of our viewControllers", traceCategories.Debug);
|
||||
}
|
||||
@ -225,7 +228,11 @@ export class TabView extends TabViewBase {
|
||||
}
|
||||
|
||||
// The "< Back" and "< More" navigation bars should not be visible simultaneously.
|
||||
let page = <Page>this.page;
|
||||
const page = this.page || this._selectedView.page || (<any>this)._selectedView.currentPage;
|
||||
if (!page || !page.frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
let actionBarVisible = page.frame._getNavBarVisible(page);
|
||||
|
||||
if (backToMoreWillBeVisible && actionBarVisible) {
|
||||
@ -251,6 +258,26 @@ export class TabView extends TabViewBase {
|
||||
}
|
||||
}
|
||||
|
||||
private getViewController(item: TabViewItem): UIViewController {
|
||||
let newController: UIViewController = item.view ? item.view.viewController : null;
|
||||
|
||||
if (newController) {
|
||||
return newController;
|
||||
}
|
||||
|
||||
if (item.view.ios instanceof UIViewController) {
|
||||
newController = item.view.ios;
|
||||
} else if (item.view.ios && item.view.ios.controller instanceof UIViewController) {
|
||||
newController = item.view.ios.controller;
|
||||
} else {
|
||||
newController = iosView.UILayoutViewController.initWithOwner(new WeakRef(item.view));
|
||||
newController.view = item.view.nativeViewProtected;
|
||||
item.view.viewController = newController;
|
||||
}
|
||||
|
||||
return newController;
|
||||
}
|
||||
|
||||
private setViewControllers(items: TabViewItem[]) {
|
||||
const length = items ? items.length : 0;
|
||||
if (length === 0) {
|
||||
@ -263,30 +290,21 @@ export class TabView extends TabViewBase {
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const item = items[i];
|
||||
let newController: UIViewController;
|
||||
|
||||
if (item.view.ios instanceof UIViewController) {
|
||||
newController = item.view.ios;
|
||||
} else {
|
||||
newController = UIViewController.new();
|
||||
newController.view.addSubview(item.view.ios);
|
||||
}
|
||||
|
||||
item.setViewController(newController);
|
||||
const controller = this.getViewController(item);
|
||||
item.setViewController(controller);
|
||||
|
||||
const icon = this._getIcon(item.iconSource);
|
||||
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((item.title || ""), icon, i);
|
||||
if (!icon) {
|
||||
updateItemTitlePosition(tabBarItem);
|
||||
}
|
||||
else if (!item.title) {
|
||||
} else if (!item.title) {
|
||||
updateItemIconPosition(tabBarItem);
|
||||
}
|
||||
|
||||
applyStatesToItem(tabBarItem, states);
|
||||
|
||||
newController.tabBarItem = tabBarItem;
|
||||
controllers.addObject(newController);
|
||||
controller.tabBarItem = tabBarItem;
|
||||
controllers.addObject(controller);
|
||||
}
|
||||
|
||||
this._ios.viewControllers = controllers;
|
||||
@ -326,52 +344,6 @@ export class TabView extends TabViewBase {
|
||||
return image;
|
||||
}
|
||||
|
||||
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
||||
const nativeView = this.nativeViewProtected;
|
||||
if (nativeView) {
|
||||
|
||||
const width = layout.getMeasureSpecSize(widthMeasureSpec);
|
||||
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
||||
|
||||
const height = layout.getMeasureSpecSize(heightMeasureSpec);
|
||||
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
||||
|
||||
this._tabBarHeight = layout.measureNativeView(this._ios.tabBar, width, widthMode, height, heightMode).height;
|
||||
const moreNavBarVisible = !!this._ios.moreNavigationController.navigationBar.window;
|
||||
this._navBarHeight = moreNavBarVisible ? layout.measureNativeView(this._ios.moreNavigationController.navigationBar, width, widthMode, height, heightMode).height : 0;
|
||||
|
||||
const density = layout.getDisplayDensity();
|
||||
let measureWidth = 0;
|
||||
let measureHeight = 0;
|
||||
|
||||
const child = this._selectedView;
|
||||
if (child) {
|
||||
const childHeightMeasureSpec = layout.makeMeasureSpec(height - this._navBarHeight - this._tabBarHeight, heightMode);
|
||||
const childSize = View.measureChild(this, child, widthMeasureSpec, childHeightMeasureSpec);
|
||||
|
||||
measureHeight = childSize.measuredHeight;
|
||||
measureWidth = childSize.measuredWidth;
|
||||
}
|
||||
|
||||
measureWidth = Math.max(measureWidth, this.effectiveMinWidth * density);
|
||||
measureHeight = Math.max(measureHeight, this.effectiveMinHeight * density);
|
||||
|
||||
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
|
||||
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
|
||||
|
||||
this.setMeasuredDimension(widthAndState, heightAndState);
|
||||
}
|
||||
}
|
||||
|
||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
||||
super.onLayout(left, top, right, bottom);
|
||||
|
||||
const child = this._selectedView;
|
||||
if (child) {
|
||||
View.layoutChild(this, child, 0, this._navBarHeight, right, (bottom - this._navBarHeight - this._tabBarHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private _updateIOSTabBarColorsAndFonts(): void {
|
||||
if (!this.items) {
|
||||
return;
|
||||
|
15
tns-core-modules/ui/utils.d.ts
vendored
15
tns-core-modules/ui/utils.d.ts
vendored
@ -1,16 +1,25 @@
|
||||
/**
|
||||
* @module "ui/utils"
|
||||
*/ /** */
|
||||
*/
|
||||
|
||||
import { View } from "./core/view";
|
||||
export module ios {
|
||||
/**
|
||||
* Gets actual height of a [UIView](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/) widget.
|
||||
* Gets actual height of a [UIView](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/) widget in device pixels.
|
||||
* @param uiView - An instance of UIView.
|
||||
*/
|
||||
export function getActualHeight(uiView: any /* UIView */): number;
|
||||
|
||||
/**
|
||||
* Deprecated.
|
||||
* @param rootView
|
||||
* @param parentBounds
|
||||
*/
|
||||
export function _layoutRootView(rootView: View, parentBounds: any /* CGRect */): void;
|
||||
|
||||
export function getStatusBarHeight(): number;
|
||||
/**
|
||||
* Gets the height of the status bar in device pixels.
|
||||
* @param viewController when specified it is used to check preferStatusBarHidden property.
|
||||
*/
|
||||
export function getStatusBarHeight(viewController?: any): number;
|
||||
}
|
||||
|
@ -5,20 +5,24 @@ import getter = utils.ios.getter;
|
||||
export module ios {
|
||||
export function getActualHeight(view: UIView): number {
|
||||
if (view.window && !view.hidden) {
|
||||
return view.frame.size.height;
|
||||
return utils.layout.toDevicePixels(view.frame.size.height);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getStatusBarHeight(): number {
|
||||
var app = getter(UIApplication, UIApplication.sharedApplication);
|
||||
export function getStatusBarHeight(viewController?: UIViewController): number {
|
||||
const app = getter(UIApplication, UIApplication.sharedApplication);
|
||||
if (!app || app.statusBarHidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var statusFrame = app.statusBarFrame;
|
||||
let min = Math.min(statusFrame.size.width, statusFrame.size.height);
|
||||
if (viewController && viewController.prefersStatusBarHidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const statusFrame = app.statusBarFrame;
|
||||
const min = Math.min(statusFrame.size.width, statusFrame.size.height);
|
||||
return utils.layout.toDevicePixels(min);
|
||||
}
|
||||
|
||||
@ -27,24 +31,19 @@ export module ios {
|
||||
return;
|
||||
}
|
||||
|
||||
let size = parentBounds.size;
|
||||
let width = utils.layout.toDevicePixels(size.width);
|
||||
let height = utils.layout.toDevicePixels(size.height);
|
||||
|
||||
var superview = (<UIView>rootView.nativeViewProtected).superview;
|
||||
var superViewRotationRadians;
|
||||
if (superview) {
|
||||
superViewRotationRadians = atan2f(superview.transform.b, superview.transform.a);
|
||||
}
|
||||
|
||||
var origin = parentBounds.origin;
|
||||
var left = origin.x;
|
||||
var top = origin.y;
|
||||
|
||||
var widthSpec = utils.layout.makeMeasureSpec(width, utils.layout.EXACTLY);
|
||||
var heightSpec = utils.layout.makeMeasureSpec(height, utils.layout.EXACTLY);
|
||||
const size = parentBounds.size;
|
||||
const width = utils.layout.toDevicePixels(size.width);
|
||||
const height = utils.layout.toDevicePixels(size.height);
|
||||
|
||||
const widthSpec = utils.layout.makeMeasureSpec(width, utils.layout.EXACTLY);
|
||||
const heightSpec = utils.layout.makeMeasureSpec(height, utils.layout.EXACTLY);
|
||||
|
||||
rootView.measure(widthSpec, heightSpec);
|
||||
|
||||
const origin = parentBounds.origin;
|
||||
const left = origin.x;
|
||||
const top = origin.y;
|
||||
|
||||
rootView.layout(left, top, width, height);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user