Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion apps/automated/src/ui/view/view-tests.ios.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import { View, Page, Button, GridLayout, Color, Utils } from '@nativescript/core';
import { View, Page, Button, GridLayout, Color, Utils, Frame } from '@nativescript/core';
import * as helper from '../../ui-helper';
import * as TKUnit from '../../tk-unit';
import { _createBackgroundColorView, _createLabelWithBorder } from './view-tests-common';

export * from './view-tests-common';

export function test_requestLayout_doesNotLoadFrameControllerView() {
const frame = new Frame();
const controller = frame.ios.controller;
TKUnit.assert(!controller.viewIfLoaded, 'A new Frame should keep its controller view unloaded.');
frame.requestLayout();
TKUnit.assert(!controller.viewIfLoaded, 'Layout invalidation must preserve lazy controller loading.');
TKUnit.assert(frame.isLayoutRequested, 'The deferred layout must remain requested.');
}

export function test_inheritedDirection_doesNotLoadFrameControllerView() {
const root = new GridLayout();
const frame = new Frame();
root.style.direction = 'ltr';
root.addChild(frame);
TKUnit.assertEqual(frame.style.direction, 'ltr', 'The Frame must inherit direction.');
TKUnit.assert(!frame.ios.controller.viewIfLoaded, 'Inherited direction must preserve lazy controller loading.');
TKUnit.assert(frame.isLayoutRequested, 'Inherited direction must still request layout.');
root.removeChild(frame);
}

class MyGrid extends GridLayout {
public backgroundDrawCount: number = 0;

Expand Down
6 changes: 4 additions & 2 deletions packages/core/ui/core/view/index.ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ export class View extends ViewCommon {
nativeView.setNeedsLayout();
}

if (this.viewController && this.viewController.view !== nativeView) {
this.viewController.view.setNeedsLayout();
// Layout invalidation must not load controllers before UIApplicationMain.
const controllerView = this.viewController?.viewIfLoaded;
if (controllerView && controllerView !== nativeView) {
controllerView.setNeedsLayout();
}

if (!requestedThisTurn) {
Expand Down
42 changes: 42 additions & 0 deletions packages/core/ui/core/view/request-layout.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,48 @@
import { StackLayout } from '../../layouts/stack-layout';

describe('iOS requestLayout', () => {
it('invalidates layout without loading an unloaded controller view', () => {
const view = new StackLayout();
const controller = new UIViewController();
const loadView = vi.spyOn(controller, 'view', 'get');
view.viewController = controller;
view.layout(0, 0, 100, 100);
expect(view.isLayoutRequested).toBe(false);

view.requestLayout();

expect(loadView).not.toHaveBeenCalled();
expect(controller.viewIfLoaded).toBeNull();
expect(view.isLayoutRequested).toBe(true);
});

it('invalidates an already loaded controller view and its distinct native view', () => {
const view = new StackLayout();
const controller = new UIViewController();
const nativeController = new UIViewController();
view.viewController = controller;
view.nativeViewProtected = nativeController.view;
const controllerLayout = vi.spyOn(controller.view, 'setNeedsLayout');
const nativeLayout = vi.spyOn(view.nativeViewProtected, 'setNeedsLayout');

view.requestLayout();

expect(controllerLayout).toHaveBeenCalledTimes(1);
expect(nativeLayout).toHaveBeenCalledTimes(1);
});

it('invalidates only once when the native view is the loaded controller view', () => {
const view = new StackLayout();
const controller = new UIViewController();
view.viewController = controller;
view.nativeViewProtected = controller.view;
const nativeLayout = vi.spyOn(controller.view, 'setNeedsLayout');

view.requestLayout();

expect(nativeLayout).toHaveBeenCalledTimes(1);
});

async function tree() {
const parent = new StackLayout();
const child = new StackLayout();
Expand Down
10 changes: 9 additions & 1 deletion packages/core/vitest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,15 @@ global.UIApplicationDidReceiveMemoryWarningNotification = 'UIApplicationDidRecei
global.UIApplicationDidChangeStatusBarOrientationNotification = 'UIApplicationDidChangeStatusBarOrientationNotification';
global.UIResponder = function () {};
global.UIResponder.extend = function () {};
global.UIViewController = function () {};
global.UIViewController = class UIViewController {
_view = null;
get view() {
return (this._view ??= { setNeedsLayout() {} });
}
get viewIfLoaded() {
return this._view;
}
};
global.UIViewControllerTransitioningDelegate = function () {};
global.UINavigationControllerDelegate = function () {};
global.UINavigationController = function () {};
Expand Down
Loading