From 2dc98277214483f6bacaf448c9c7870b4d1597b6 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Mon, 25 Jul 2016 11:47:07 +0300 Subject: [PATCH] FIX: Wrong isBackNavigation value --- tests/app/navigation/navigation-tests.ts | 46 ++++++++++++++++++++++++ tns-core-modules/ui/frame/frame.ios.ts | 7 ++++ tns-core-modules/ui/page/page.ios.ts | 38 ++++++++++++++------ 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/tests/app/navigation/navigation-tests.ts b/tests/app/navigation/navigation-tests.ts index 1bf3f7140..f55b1ad88 100644 --- a/tests/app/navigation/navigation-tests.ts +++ b/tests/app/navigation/navigation-tests.ts @@ -382,4 +382,50 @@ export function test_NavigationEvents_WithBackstackVisibile_False_Forward_Forwar export function test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward_WithTransition() { androidGC(); _test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward({ name: "fade" }); +} + +function _test_NavigationEvents_WithClearHistory(transition?: NavigationTransition) { + const topmost = topmostFrame(); + const mainTestPage = topmost.currentPage; + + mainTestPage.id = "main-page"; + let actualMainPageEvents = new Array(); + attachEventListeners(mainTestPage, actualMainPageEvents); + + let actualSecondPageEvents = new Array(); + const secondPage = new Page(); + let secondPageFactory = function (): Page { + secondPage.actionBarHidden = true; + secondPage.id = "second-page"; + attachEventListeners(secondPage, actualSecondPageEvents); + secondPage.style.backgroundColor = new Color(255, Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)); + return secondPage; + }; + + // Go to second page + helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: true, clearHistory: true }); + + let expectedMainPageEvents = [ + "main-page navigatingFrom forward", + "main-page navigatedFrom forward" + ]; + TKUnit.arrayAssert(actualMainPageEvents, expectedMainPageEvents, "Actual main-page events are different from expected."); + + let expectedSecondPageEvents = [ + "second-page navigatingTo forward", + "second-page navigatedTo forward", + ]; + TKUnit.arrayAssert(actualSecondPageEvents, expectedSecondPageEvents, "Actual main-page events are different from expected."); + + TKUnit.assertEqual(topmost.currentPage, secondPage, "We should be on the second page at the end of the test."); +} + +export function test_NavigationEvents_WithClearHistory() { + androidGC(); + _test_NavigationEvents_WithClearHistory(); +} + +export function test_NavigationEvents_WithClearHistory_WithTransition() { + androidGC(); + _test_NavigationEvents_WithClearHistory({ name: "fade" }); } \ No newline at end of file diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index 560981f7d..3c21519e4 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -125,6 +125,13 @@ export class Frame extends frameCommon.Frame { viewController.navigationItem.hidesBackButton = true; let newControllers = NSMutableArray.alloc().initWithCapacity(1); newControllers.addObject(viewController); + + // Mark all previous ViewControllers as cleared + const oldControllers = this._ios.controller.viewControllers; + for (let i = 0; i < oldControllers.count; i++) { + oldControllers.objectAtIndex(i).isBackstackCleared = true; + } + this._ios.controller.setViewControllersAnimated(newControllers, animated); if (trace.enabled) { trace.write(`${this}.setViewControllersAnimated([${viewController}], ${animated}); depth = ${navDepth}`, trace.categories.Navigation); diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index 2a7f692f8..1b8444e72 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -7,10 +7,10 @@ import {device} from "platform"; import {DeviceType} from "ui/enums"; global.moduleMerge(pageCommon, exports); -var ENTRY = "_entry"; -var DELEGATE = "_delegate"; +const ENTRY = "_entry"; +const DELEGATE = "_delegate"; -function isBackNavigation(page: Page, entry): boolean { +function isBackNavigationTo(page: Page, entry): boolean { let frame = page.frame; if (!frame) { return false; @@ -21,7 +21,7 @@ function isBackNavigation(page: Page, entry): boolean { } else { let navigationQueue = (frame)._navigationQueue; - for (var i = 0; i < navigationQueue.length; i++) { + for (let i = 0; i < navigationQueue.length; i++) { if (navigationQueue[i].entry === entry) { return navigationQueue[i].isBackNavigation; } @@ -31,11 +31,29 @@ function isBackNavigation(page: Page, entry): boolean { return false; } +function isBackNavigationFrom(controller: UIViewControllerImpl, page: Page): boolean { + if (!page.frame) { + return false; + } + + // Controller is cleared or backstack skipped + if (controller.isBackstackCleared || controller.isBackstackSkipped) { + return false; + } + + if (controller.navigationController && controller.navigationController.viewControllers.containsObject(controller)) { + return false; + } + + return true; +} + class UIViewControllerImpl extends UIViewController { private _owner: WeakRef; public isBackstackSkipped: boolean; + public isBackstackCleared: boolean; public static initWithOwner(owner: WeakRef): UIViewControllerImpl { let controller = UIViewControllerImpl.new(); @@ -130,7 +148,7 @@ class UIViewControllerImpl extends UIViewController { // Don't raise event if currentPage was showing modal page. if (!page._presentedViewController && newEntry && (!frame || frame.currentPage !== page)) { - let isBack = isBackNavigation(page, newEntry) + let isBack = isBackNavigationTo(page, newEntry); page.onNavigatingTo(newEntry.entry.context, isBack, newEntry.entry.bindingContext); } @@ -179,7 +197,7 @@ class UIViewControllerImpl extends UIViewController { // Skip navigation events if modal page is shown. if (!page._presentedViewController && frame) { let newEntry = this[ENTRY]; - let isBack = isBackNavigation(page, newEntry); + let isBack = isBackNavigationTo(page, 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) { isBack = false; @@ -224,10 +242,10 @@ class UIViewControllerImpl extends UIViewController { page._presentedViewController = this.presentedViewController; } - var frame = page.frame; + const frame = page.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 = page.frame && (!this.navigationController || !this.navigationController.viewControllers.containsObject(this)) && !this.isBackstackSkipped; + let isBack = isBackNavigationFrom(this, page); page.onNavigatingFrom(isBack); } @@ -267,7 +285,7 @@ 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 = frame && (!this.navigationController || !this.navigationController.viewControllers.containsObject(this)) && !this.isBackstackSkipped; + let isBack = isBackNavigationFrom(this, page); if (isBack) { // Remove parent when navigating back. frame._removeView(page); @@ -398,7 +416,7 @@ export class Page extends pageCommon.Page { } public _updateActionBar(hidden: boolean) { - var frame = this.frame; + const frame = this.frame; if (frame) { frame._updateActionBar(this); }