diff --git a/apps/automated/src/test-runner.ts b/apps/automated/src/test-runner.ts index 014ef2447..85cd7c892 100644 --- a/apps/automated/src/test-runner.ts +++ b/apps/automated/src/test-runner.ts @@ -138,19 +138,19 @@ allTests['STACKLAYOUT'] = stackLayoutTests; import * as flexBoxLayoutTests from './ui/layouts/flexbox-layout-tests'; allTests['FLEXBOXLAYOUT'] = flexBoxLayoutTests; -// import * as safeAreaLayoutTests from './ui/layouts/safe-area-tests'; -// import * as safeAreaListViewtTests from './ui/list-view/list-view-safe-area-tests'; -// import * as scrollViewSafeAreaTests from './ui/scroll-view/scroll-view-safe-area-tests'; -// import * as repeaterSafeAreaTests from './ui/repeater/repeater-safe-area-tests'; -// import * as webViewSafeAreaTests from './ui/web-view/web-view-safe-area-tests'; +import * as safeAreaLayoutTests from './ui/layouts/safe-area-tests'; +import * as safeAreaListViewtTests from './ui/list-view/list-view-safe-area-tests'; +import * as scrollViewSafeAreaTests from './ui/scroll-view/scroll-view-safe-area-tests'; +import * as repeaterSafeAreaTests from './ui/repeater/repeater-safe-area-tests'; +import * as webViewSafeAreaTests from './ui/web-view/web-view-safe-area-tests'; -// if (isIOS && Utils.ios.MajorVersion > 10) { -// allTests['SAFEAREALAYOUT'] = safeAreaLayoutTests; -// allTests['SAFEAREA-LISTVIEW'] = safeAreaListViewtTests; -// allTests['SAFEAREA-SCROLL-VIEW'] = scrollViewSafeAreaTests; -// allTests['SAFEAREA-REPEATER'] = repeaterSafeAreaTests; -// allTests['SAFEAREA-WEBVIEW'] = webViewSafeAreaTests; -// } +if (isIOS && Utils.ios.MajorVersion > 10) { + allTests['SAFEAREALAYOUT'] = safeAreaLayoutTests; + allTests['SAFEAREA-LISTVIEW'] = safeAreaListViewtTests; + allTests['SAFEAREA-SCROLL-VIEW'] = scrollViewSafeAreaTests; + allTests['SAFEAREA-REPEATER'] = repeaterSafeAreaTests; + allTests['SAFEAREA-WEBVIEW'] = webViewSafeAreaTests; +} import * as rootViewsCssClassesTests from './ui/styling/root-views-css-classes-tests'; allTests['ROOT-VIEWS-CSS-CLASSES'] = rootViewsCssClassesTests; diff --git a/packages/core/ui/core/view/view-helper/index.ios.ts b/packages/core/ui/core/view/view-helper/index.ios.ts index 773c6e66b..4f3de5616 100644 --- a/packages/core/ui/core/view/view-helper/index.ios.ts +++ b/packages/core/ui/core/view/view-helper/index.ios.ts @@ -92,61 +92,62 @@ class UILayoutViewController extends UIViewController { } layoutOwner(force = false) { const owner = this.owner?.deref(); - if (!force && !!owner.nativeViewProtected?.layer.needsLayout?.()) { + if (!owner) { + return; + } + if (!force && owner.isLayoutValid && !owner.nativeViewProtected?.layer.needsLayout?.()) { // we skip layout if the view is not yet laid out yet // this usually means that viewDidLayoutSubviews will be called again // so doing a layout pass now will layout with the wrong parameters return; } - if (owner) { - if (majorVersion >= 11) { - // Handle nested UILayoutViewController safe area application. - // Currently, UILayoutViewController can be nested only in a TabView. - // The TabView itself is handled by the OS, so we check the TabView's parent (usually a Page, but can be a Layout). - const tabViewItem = owner.parent; - const tabView = tabViewItem && tabViewItem.parent; - let parent = tabView && tabView.parent; + if (majorVersion >= 11) { + // Handle nested UILayoutViewController safe area application. + // Currently, UILayoutViewController can be nested only in a TabView. + // The TabView itself is handled by the OS, so we check the TabView's parent (usually a Page, but can be a Layout). + const tabViewItem = owner.parent; + const tabView = tabViewItem && tabViewItem.parent; + let parent = tabView && tabView.parent; - // Handle Angular scenario where TabView is in a ProxyViewContainer - // It is possible to wrap components in ProxyViewContainers indefinitely - // Not using instanceof ProxyViewContainer to avoid circular dependency - // TODO: Try moving UILayoutViewController out of view module - while (parent && !parent.nativeViewProtected) { - parent = parent.parent; - } - - if (parent) { - const parentPageInsetsTop = parent.nativeViewProtected.safeAreaInsets.top; - const parentPageInsetsBottom = parent.nativeViewProtected.safeAreaInsets.bottom; - let currentInsetsTop = this.view.safeAreaInsets.top; - let currentInsetsBottom = this.view.safeAreaInsets.bottom; - - // Safe area insets include additional safe area insets too, so subtract old values - if (this.additionalSafeAreaInsets) { - currentInsetsTop -= this.additionalSafeAreaInsets.top; - currentInsetsBottom -= this.additionalSafeAreaInsets.bottom; - } - - const additionalInsetsTop = Math.max(parentPageInsetsTop - currentInsetsTop, 0); - const additionalInsetsBottom = Math.max(parentPageInsetsBottom - currentInsetsBottom, 0); - - if (additionalInsetsTop > 0 || additionalInsetsBottom > 0) { - const additionalInsets = new UIEdgeInsets({ - top: additionalInsetsTop, - left: 0, - bottom: additionalInsetsBottom, - right: 0, - }); - this.additionalSafeAreaInsets = additionalInsets; - } else { - this.additionalSafeAreaInsets = null; - } - } + // Handle Angular scenario where TabView is in a ProxyViewContainer + // It is possible to wrap components in ProxyViewContainers indefinitely + // Not using instanceof ProxyViewContainer to avoid circular dependency + // TODO: Try moving UILayoutViewController out of view module + while (parent && !parent.nativeViewProtected) { + parent = parent.parent; } - IOSHelper.layoutView(this, owner); + if (parent) { + const parentPageInsetsTop = parent.nativeViewProtected.safeAreaInsets.top; + const parentPageInsetsBottom = parent.nativeViewProtected.safeAreaInsets.bottom; + let currentInsetsTop = this.view.safeAreaInsets.top; + let currentInsetsBottom = this.view.safeAreaInsets.bottom; + + // Safe area insets include additional safe area insets too, so subtract old values + if (this.additionalSafeAreaInsets) { + currentInsetsTop -= this.additionalSafeAreaInsets.top; + currentInsetsBottom -= this.additionalSafeAreaInsets.bottom; + } + + const additionalInsetsTop = Math.max(parentPageInsetsTop - currentInsetsTop, 0); + const additionalInsetsBottom = Math.max(parentPageInsetsBottom - currentInsetsBottom, 0); + + if (additionalInsetsTop > 0 || additionalInsetsBottom > 0) { + const additionalInsets = new UIEdgeInsets({ + top: additionalInsetsTop, + left: 0, + bottom: additionalInsetsBottom, + right: 0, + }); + this.additionalSafeAreaInsets = additionalInsets; + } else { + this.additionalSafeAreaInsets = null; + } + } } + + IOSHelper.layoutView(this, owner); } public viewWillAppear(animated: boolean): void { diff --git a/packages/core/ui/page/index.ios.ts b/packages/core/ui/page/index.ios.ts index 6f61bcc8c..7d5ad4925 100644 --- a/packages/core/ui/page/index.ios.ts +++ b/packages/core/ui/page/index.ios.ts @@ -312,63 +312,65 @@ class UIViewControllerImpl extends UIViewController { layoutOwner(force = false) { const owner = this._owner?.deref(); - if (!force && !!owner.nativeViewProtected?.layer.needsLayout?.()) { + if (!owner) { + return; + } + if (!force && owner.isLayoutValid && !owner.nativeViewProtected?.layer.needsLayout?.()) { // we skip layout if the view is not yet laid out yet // this usually means that viewDidLayoutSubviews will be called again // so doing a layout pass now will layout with the wrong parameters return; } - if (owner) { - // layout(owner.actionBar) - // layout(owner.content) - if (majorVersion >= 11) { - // Handle nested Page safe area insets application. - // A Page is nested if its Frame has a parent. - // If the Page is nested, cross check safe area insets on top and bottom with Frame parent. - const frame = owner.parent; - // There is a legacy scenario where Page is not in a Frame - the root of a Modal View, so it has no parent. - let frameParent = frame && frame.parent; + // layout(owner.actionBar) + // layout(owner.content) - // Handle Angular scenario where TabView is in a ProxyViewContainer - // It is possible to wrap components in ProxyViewContainers indefinitely - // Not using instanceof ProxyViewContainer to avoid circular dependency - // TODO: Try moving UIViewControllerImpl out of page module - while (frameParent && !frameParent.nativeViewProtected) { - frameParent = frameParent.parent; - } + if (majorVersion >= 11) { + // Handle nested Page safe area insets application. + // A Page is nested if its Frame has a parent. + // If the Page is nested, cross check safe area insets on top and bottom with Frame parent. + const frame = owner.parent; + // There is a legacy scenario where Page is not in a Frame - the root of a Modal View, so it has no parent. + let frameParent = frame && frame.parent; - if (frameParent) { - const parentPageInsetsTop = frameParent.nativeViewProtected.safeAreaInsets.top; - const parentPageInsetsBottom = frameParent.nativeViewProtected.safeAreaInsets.bottom; - let currentInsetsTop = this.view.safeAreaInsets.top; - let currentInsetsBottom = this.view.safeAreaInsets.bottom; - - // Safe area insets include additional safe area insets too, so subtract old values - if (this.additionalSafeAreaInsets) { - currentInsetsTop -= this.additionalSafeAreaInsets.top; - currentInsetsBottom -= this.additionalSafeAreaInsets.bottom; - } - - const additionalInsetsTop = Math.max(parentPageInsetsTop - currentInsetsTop, 0); - const additionalInsetsBottom = Math.max(parentPageInsetsBottom - currentInsetsBottom, 0); - - if (additionalInsetsTop > 0 || additionalInsetsBottom > 0) { - const additionalInsets = new UIEdgeInsets({ - top: additionalInsetsTop, - left: 0, - bottom: additionalInsetsBottom, - right: 0, - }); - this.additionalSafeAreaInsets = additionalInsets; - } else { - this.additionalSafeAreaInsets = null; - } - } + // Handle Angular scenario where TabView is in a ProxyViewContainer + // It is possible to wrap components in ProxyViewContainers indefinitely + // Not using instanceof ProxyViewContainer to avoid circular dependency + // TODO: Try moving UIViewControllerImpl out of page module + while (frameParent && !frameParent.nativeViewProtected) { + frameParent = frameParent.parent; } - IOSHelper.layoutView(this, owner); + if (frameParent) { + const parentPageInsetsTop = frameParent.nativeViewProtected.safeAreaInsets.top; + const parentPageInsetsBottom = frameParent.nativeViewProtected.safeAreaInsets.bottom; + let currentInsetsTop = this.view.safeAreaInsets.top; + let currentInsetsBottom = this.view.safeAreaInsets.bottom; + + // Safe area insets include additional safe area insets too, so subtract old values + if (this.additionalSafeAreaInsets) { + currentInsetsTop -= this.additionalSafeAreaInsets.top; + currentInsetsBottom -= this.additionalSafeAreaInsets.bottom; + } + + const additionalInsetsTop = Math.max(parentPageInsetsTop - currentInsetsTop, 0); + const additionalInsetsBottom = Math.max(parentPageInsetsBottom - currentInsetsBottom, 0); + + if (additionalInsetsTop > 0 || additionalInsetsBottom > 0) { + const additionalInsets = new UIEdgeInsets({ + top: additionalInsetsTop, + left: 0, + bottom: additionalInsetsBottom, + right: 0, + }); + this.additionalSafeAreaInsets = additionalInsets; + } else { + this.additionalSafeAreaInsets = null; + } + } } + + IOSHelper.layoutView(this, owner); } // Mind implementation for other controllerss