fix(tabs): delay loadView when animation runs (#8353)

* fix(tabs): delay loadView when animation runs

* chore: update api.md

* chore: remove unnecessary casting

* test: Added disabled test for changing tabs
This commit is contained in:
Vasil Trifonov
2020-02-21 16:47:33 +02:00
committed by GitHub
parent dd11158374
commit e649a6cfd6
14 changed files with 104 additions and 10 deletions

View File

@ -772,6 +772,9 @@ export class Frame extends View {
animated: boolean; animated: boolean;
// (undocumented)
_animationInProgress: boolean;
backStack: Array<BackstackEntry>; backStack: Array<BackstackEntry>;
canGoBack(): boolean; canGoBack(): boolean;
@ -2931,6 +2934,7 @@ export abstract class ViewBase extends Observable {
// (undocumented) // (undocumented)
_setupAsRootView(context: any): void; _setupAsRootView(context: any): void;
_setupUI(context: any /* android.content.Context */, atIndex?: number): void; _setupUI(context: any /* android.content.Context */, atIndex?: number): void;
_shouldDelayLoad(): boolean;
showModal(moduleName: string, modalOptions: ShowModalOptions): ViewBase; showModal(moduleName: string, modalOptions: ShowModalOptions): ViewBase;
showModal(view: ViewBase, modalOptions: ShowModalOptions): ViewBase; showModal(view: ViewBase, modalOptions: ShowModalOptions): ViewBase;
public readonly style: Style; public readonly style: Style;

View File

@ -0,0 +1,3 @@
<Page class="page" >
<Label text="Inner label 1" backgroundColor="blue"/>
</Page>

View File

@ -0,0 +1,3 @@
<Page class="page">
<Label text="Inner label 2" backgroundColor="red"/>
</Page>

View File

@ -0,0 +1,3 @@
<Page class="page">
<Label text="Inner label 3" backgroundColor="orange"/>
</Page>

View File

@ -0,0 +1,3 @@
<Page class="page">
<Label text="Inner label 4" backgroundColor="green"/>
</Page>

View File

@ -0,0 +1,3 @@
export function onItemTap(args) {
console.log(`Item with index: ${args.index} tapped`);
}

View File

@ -0,0 +1,24 @@
<Page class="page">
<StackLayout>
<Tabs highlightColor="red" offscreenTabLimit="1">
<TabStrip highlightColor="green" itemTap="onItemTap">
<TabStripItem title="1"></TabStripItem>
<TabStripItem title="2"></TabStripItem>
<TabStripItem title="3"></TabStripItem>
<TabStripItem title="4"></TabStripItem>
</TabStrip>
<TabContentItem>
<Frame defaultPage="tabs/frame-in-tabs-inner-page-1"></Frame>
</TabContentItem>
<TabContentItem>
<Frame defaultPage="tabs/frame-in-tabs-inner-page-2"></Frame>
</TabContentItem>
<TabContentItem>
<Frame defaultPage="tabs/frame-in-tabs-inner-page-3"></Frame>
</TabContentItem>
<TabContentItem>
<Frame defaultPage="tabs/frame-in-tabs-inner-page-4"></Frame>
</TabContentItem>
</Tabs>
</StackLayout>
</Page>

View File

@ -283,4 +283,22 @@ describe(`${imagePrefix}-suite`, async function () {
assert.isTrue(driver.imageHelper.hasImageComparisonPassed()); assert.isTrue(driver.imageHelper.hasImageComparisonPassed());
await tabsViewBasePage.navigateBackToSuitMainPage(); await tabsViewBasePage.navigateBackToSuitMainPage();
}); });
// it(`${imagePrefix}-frame-in-tabs`, async function () {
// await tabsViewBasePage.navigateToSample("frame-in-tabs");
// await driver.imageHelper.compareScreen();
// // go through the tabs and check that they are loaded
// await tabsViewBasePage.tabOnItem(1);
// await driver.imageHelper.compareScreen();
// await tabsViewBasePage.tabOnItem(2);
// await driver.imageHelper.compareScreen();
// await tabsViewBasePage.tabOnItem(3);
// await driver.imageHelper.compareScreen();
// assert.isTrue(driver.imageHelper.hasImageComparisonPassed());
// await tabsViewBasePage.navigateBackToSuitMainPage();
// });
}); });

View File

@ -470,6 +470,12 @@ export abstract class ViewBase extends Observable {
*/ */
_setupAsRootView(context: any): void; _setupAsRootView(context: any): void;
/**
* When returning true the callLoaded method will be run in setTimeout
* Method is intended to be overridden by inheritors and used as "protected"
*/
_shouldDelayLoad(): boolean;
/** /**
* @private * @private
*/ */

View File

@ -615,10 +615,18 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
public loadView(view: ViewBase): void { public loadView(view: ViewBase): void {
if (view && !view.isLoaded) { if (view && !view.isLoaded) {
view.callLoaded(); if (this._shouldDelayLoad()) {
setTimeout(() => view.callLoaded());
} else {
view.callLoaded();
}
} }
} }
public _shouldDelayLoad(): boolean {
return false;
}
public unloadView(view: ViewBase): void { public unloadView(view: ViewBase): void {
if (view && view.isLoaded) { if (view && view.isLoaded) {
view.callUnloaded(); view.callUnloaded();
@ -1053,7 +1061,7 @@ export const classNameProperty = new Property<ViewBase, string>({
cssClasses.clear(); cssClasses.clear();
if (shouldAddModalRootViewCssClasses) { if (shouldAddModalRootViewCssClasses) {
cssClasses.add(MODAL_ROOT_VIEW_CSS_CLASS); cssClasses.add(MODAL_ROOT_VIEW_CSS_CLASS);
} else if (shouldAddRootViewCssClasses) { } else if (shouldAddRootViewCssClasses) {
cssClasses.add(ROOT_VIEW_CSS_CLASS); cssClasses.add(ROOT_VIEW_CSS_CLASS);
} }

View File

@ -42,6 +42,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
public actionBarVisibility: "auto" | "never" | "always"; public actionBarVisibility: "auto" | "never" | "always";
public _currentEntry: BackstackEntry; public _currentEntry: BackstackEntry;
public _animationInProgress = false;
public _executingContext: NavigationContext; public _executingContext: NavigationContext;
public _isInFrameStack = false; public _isInFrameStack = false;
public static defaultAnimatedNavigation = true; public static defaultAnimatedNavigation = true;

View File

@ -147,6 +147,10 @@ export class Frame extends View {
* @private * @private
*/ */
navigationBarHeight: number; navigationBarHeight: number;
/**
* @private
*/
_animationInProgress: boolean;
/** /**
* @private * @private
*/ */
@ -218,21 +222,21 @@ export function setFragmentClass(clazz: any): void;
/** /**
* @deprecated Use Frame.getFrameById() instead. * @deprecated Use Frame.getFrameById() instead.
* *
* Gets a frame by id. * Gets a frame by id.
*/ */
export function getFrameById(id: string): Frame; export function getFrameById(id: string): Frame;
/** /**
* @deprecated Use Frame.topmost() instead. * @deprecated Use Frame.topmost() instead.
* *
* Gets the topmost frame in the frames stack. An application will typically has one frame instance. Multiple frames handle nested (hierarchical) navigation scenarios. * Gets the topmost frame in the frames stack. An application will typically has one frame instance. Multiple frames handle nested (hierarchical) navigation scenarios.
*/ */
export function topmost(): Frame; export function topmost(): Frame;
/** /**
* @deprecated Use Frame.goBack() instead. * @deprecated Use Frame.goBack() instead.
* *
* Navigates back using the navigation hierarchy (if any). Updates the Frame stack as needed. * Navigates back using the navigation hierarchy (if any). Updates the Frame stack as needed.
* This method will start from the topmost Frame and will recursively search for an instance that has the canGoBack operation available. * This method will start from the topmost Frame and will recursively search for an instance that has the canGoBack operation available.
*/ */
@ -241,7 +245,7 @@ export function goBack();
//@private //@private
/** /**
* @deprecated Use Frame._stack() instead. * @deprecated Use Frame._stack() instead.
* *
* @private * @private
*/ */
export function _stack(): Array<Frame>; export function _stack(): Array<Frame>;
@ -487,7 +491,7 @@ export function setActivityCallbacks(activity: any /*androidx.appcompat.app.AppC
//@private //@private
/** /**
* @deprecated Use Frame.reloadPage() instead. * @deprecated Use Frame.reloadPage() instead.
* *
* @private * @private
*/ */
export function reloadPage(context?: ModuleContext): void; export function reloadPage(context?: ModuleContext): void;

View File

@ -93,7 +93,7 @@ class UIViewControllerImpl extends UIViewController {
return; return;
} }
const frame = this.navigationController ? (<any>this.navigationController).owner : null; const frame = (this.navigationController ? (<any>this.navigationController).owner : null);
const newEntry = this[ENTRY]; const newEntry = this[ENTRY];
// Don't raise event if currentPage was showing modal page. // Don't raise event if currentPage was showing modal page.
@ -199,7 +199,7 @@ class UIViewControllerImpl extends UIViewController {
if (!owner) { if (!owner) {
return; return;
} }
// Cache presentedViewController if any. We don't want to raise // Cache presentedViewController if any. We don't want to raise
// navigation events in case of presenting view controller. // navigation events in case of presenting view controller.
if (!owner._presentedViewController) { if (!owner._presentedViewController) {
@ -230,7 +230,6 @@ class UIViewControllerImpl extends UIViewController {
if (!page || page.modal || page._presentedViewController) { if (!page || page.modal || page._presentedViewController) {
return; return;
} }
// Forward navigation does not remove page from frame so we raise unloaded manually. // Forward navigation does not remove page from frame so we raise unloaded manually.
if (page.isLoaded) { if (page.isLoaded) {
page.callUnloaded(); page.callUnloaded();
@ -349,6 +348,10 @@ export class Page extends PageBase {
// //
} }
public _shouldDelayLoad(): boolean {
return this._frame && this._frame._animationInProgress;
}
public onLoaded(): void { public onLoaded(): void {
super.onLoaded(); super.onLoaded();
if (this.hasActionBar) { if (this.hasActionBar) {

View File

@ -1091,7 +1091,18 @@ export class Tabs extends TabsBase {
} }
this._currentNativeSelectedIndex = value; this._currentNativeSelectedIndex = value;
let itemControllerOwner = null;
if (itemController._owner) {
let itemControllerOwner = <Frame>itemController._owner.get();
// do not load new views while the animation is in progress https://stackoverflow.com/a/47031524/613113
itemControllerOwner._animationInProgress = true;
}
this.viewController.setViewControllersDirectionAnimatedCompletion(controllers, navigationDirection, true, (finished: boolean) => { this.viewController.setViewControllersDirectionAnimatedCompletion(controllers, navigationDirection, true, (finished: boolean) => {
if (itemControllerOwner) {
itemControllerOwner._animationInProgress = false;
}
if (finished) { if (finished) {
// HACK: UIPageViewController fix; see https://stackoverflow.com/a/17330606 // HACK: UIPageViewController fix; see https://stackoverflow.com/a/17330606
invokeOnRunLoop(() => this.viewController.setViewControllersDirectionAnimatedCompletion(controllers, navigationDirection, false, null)); invokeOnRunLoop(() => this.viewController.setViewControllersDirectionAnimatedCompletion(controllers, navigationDirection, false, null));