From fd77f3dc1d68e42570213472370db71a1bb42496 Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Wed, 18 Dec 2024 19:14:16 +0200 Subject: [PATCH 1/4] fix(ios): Incorrect background styles after frame changed by safe area --- packages/core/ui/core/view/index.ios.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/core/ui/core/view/index.ios.ts b/packages/core/ui/core/view/index.ios.ts index 3465ce4f8c..5d2f2a88df 100644 --- a/packages/core/ui/core/view/index.ios.ts +++ b/packages/core/ui/core/view/index.ios.ts @@ -108,19 +108,28 @@ export class View extends ViewCommon implements ViewDefinition { @profile public layout(left: number, top: number, right: number, bottom: number, setFrame = true): void { - const { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom); + const result = this._setCurrentLayoutBounds(left, top, right, bottom); + let { sizeChanged } = result; + if (setFrame) { this.layoutNativeView(left, top, right, bottom); } - const needsLayout = boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED; + const needsLayout = result.boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED; if (needsLayout) { let position = { left, top, right, bottom }; + if (this.nativeViewProtected && SDK_VERSION > 10) { // on iOS 11+ it is possible to have a changed layout frame due to safe area insets // get the frame and adjust the position, so that onLayout works correctly - const frame = this.nativeViewProtected.frame; - position = IOSHelper.getPositionFromFrame(frame); + position = IOSHelper.getPositionFromFrame(this.nativeViewProtected.frame); + + // If frame has actually changed, there is the need to update view background and border styles as they depend on native view bounds + // To trigger the needed visual update, mark size as changed + const positionChanged = position.left !== left || position.top !== top || position.right !== right || position.bottom !== bottom; + if (positionChanged && !sizeChanged) { + sizeChanged = true; + } } this.onLayout(position.left, position.top, position.right, position.bottom); From 32ac7b3d9367bcc0ddd1bbcdef432ccc630f632e Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Wed, 18 Dec 2024 19:41:49 +0200 Subject: [PATCH 2/4] chore: Types cleanup --- packages/core/ui/core/view/index.android.ts | 9 ++------- packages/core/ui/core/view/index.d.ts | 14 ++++++++------ packages/core/ui/core/view/index.ios.ts | 13 +++++-------- packages/core/ui/core/view/view-common.ts | 9 ++------- .../core/ui/layouts/flexbox-layout/index.ios.ts | 6 +++--- packages/core/ui/layouts/stack-layout/index.ios.ts | 6 +++--- packages/core/ui/styling/background.ios.ts | 11 +---------- 7 files changed, 24 insertions(+), 44 deletions(-) diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index 621161b23c..3e3178e762 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -1,5 +1,5 @@ // Definitions. -import type { Point, CustomLayoutView as CustomLayoutViewDefinition } from '.'; +import type { Point, CustomLayoutView as CustomLayoutViewDefinition, Position } from '.'; import type { GestureTypes, GestureEventData } from '../../gestures'; // Types. @@ -577,12 +577,7 @@ export class View extends ViewCommon { } } - _getCurrentLayoutBounds(): { - left: number; - top: number; - right: number; - bottom: number; - } { + _getCurrentLayoutBounds(): Position { if (this.nativeViewProtected && !this.isCollapsed) { return { left: this.nativeViewProtected.getLeft(), diff --git a/packages/core/ui/core/view/index.d.ts b/packages/core/ui/core/view/index.d.ts index 3622219cf7..a9c58237e5 100644 --- a/packages/core/ui/core/view/index.d.ts +++ b/packages/core/ui/core/view/index.d.ts @@ -64,6 +64,13 @@ export interface Point { z?: number; } +export interface Position { + top: number; + right: number; + bottom: number; + left: number; +} + /** * The Size interface describes abstract dimensions in two dimensional space. * It has two properties width and height, representing the width and height values of the size. @@ -817,12 +824,7 @@ export abstract class View extends ViewCommon { * Return view bounds. * @private */ - _getCurrentLayoutBounds(): { - left: number; - top: number; - right: number; - bottom: number; - }; + _getCurrentLayoutBounds(): Position; /** * @private */ diff --git a/packages/core/ui/core/view/index.ios.ts b/packages/core/ui/core/view/index.ios.ts index 5d2f2a88df..028242a647 100644 --- a/packages/core/ui/core/view/index.ios.ts +++ b/packages/core/ui/core/view/index.ios.ts @@ -1,5 +1,5 @@ // Types. -import { Point, View as ViewDefinition } from '.'; +import { Point, Position, View as ViewDefinition } from '.'; // Requires import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common'; @@ -117,7 +117,7 @@ export class View extends ViewCommon implements ViewDefinition { const needsLayout = result.boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED; if (needsLayout) { - let position = { left, top, right, bottom }; + let position: Position; if (this.nativeViewProtected && SDK_VERSION > 10) { // on iOS 11+ it is possible to have a changed layout frame due to safe area insets @@ -130,6 +130,8 @@ export class View extends ViewCommon implements ViewDefinition { if (positionChanged && !sizeChanged) { sizeChanged = true; } + } else { + position = { left, top, right, bottom }; } this.onLayout(position.left, position.top, position.right, position.bottom); @@ -947,12 +949,7 @@ export class View extends ViewCommon implements ViewDefinition { }); } - _getCurrentLayoutBounds(): { - left: number; - top: number; - right: number; - bottom: number; - } { + _getCurrentLayoutBounds(): Position { const nativeView = this.nativeViewProtected; if (nativeView && !this.isCollapsed) { const frame = nativeView.frame; diff --git a/packages/core/ui/core/view/view-common.ts b/packages/core/ui/core/view/view-common.ts index d23ae92bc3..e64f4734f9 100644 --- a/packages/core/ui/core/view/view-common.ts +++ b/packages/core/ui/core/view/view-common.ts @@ -1,5 +1,5 @@ // Definitions. -import { View as ViewDefinition, Point, Size, ShownModallyData } from '.'; +import { View as ViewDefinition, Point, Size, ShownModallyData, Position } from '.'; import { booleanConverter, ShowModalOptions, ViewBase } from '../view-base'; import { getEventOrGestureName } from '../bindable'; @@ -1048,12 +1048,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition { return changed; } - _getCurrentLayoutBounds(): { - left: number; - top: number; - right: number; - bottom: number; - } { + _getCurrentLayoutBounds(): Position { return { left: 0, top: 0, right: 0, bottom: 0 }; } diff --git a/packages/core/ui/layouts/flexbox-layout/index.ios.ts b/packages/core/ui/layouts/flexbox-layout/index.ios.ts index 9ac6fafc8c..22b3913de8 100644 --- a/packages/core/ui/layouts/flexbox-layout/index.ios.ts +++ b/packages/core/ui/layouts/flexbox-layout/index.ios.ts @@ -1,5 +1,5 @@ import { FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, FlexboxLayoutBase, FlexBasisPercent, orderProperty, flexGrowProperty, flexShrinkProperty, flexWrapBeforeProperty, alignSelfProperty } from './flexbox-layout-common'; -import { View } from '../../core/view'; +import { Position, View } from '../../core/view'; import { layout } from '../../../utils'; export * from './flexbox-layout-common'; @@ -977,7 +977,7 @@ export class FlexboxLayout extends FlexboxLayoutBase { } } - private _layoutHorizontal(isRtl: boolean, left: number, top: number, right: number, bottom: number, insets: { left; top; right; bottom }) { + private _layoutHorizontal(isRtl: boolean, left: number, top: number, right: number, bottom: number, insets: Position) { // include insets const paddingLeft = this.effectivePaddingLeft + insets.left; const paddingTop = this.effectivePaddingTop + insets.top; @@ -1122,7 +1122,7 @@ export class FlexboxLayout extends FlexboxLayoutBase { } } - private _layoutVertical(isRtl: boolean, fromBottomToTop: boolean, left: number, top: number, right: number, bottom: number, insets: { left; top; right; bottom }) { + private _layoutVertical(isRtl: boolean, fromBottomToTop: boolean, left: number, top: number, right: number, bottom: number, insets: Position) { const paddingLeft = this.effectivePaddingLeft + insets.left; const paddingTop = this.effectivePaddingTop + insets.top; const paddingRight = this.effectivePaddingRight + insets.right; diff --git a/packages/core/ui/layouts/stack-layout/index.ios.ts b/packages/core/ui/layouts/stack-layout/index.ios.ts index 23af3e41c1..64e347d699 100644 --- a/packages/core/ui/layouts/stack-layout/index.ios.ts +++ b/packages/core/ui/layouts/stack-layout/index.ios.ts @@ -1,6 +1,6 @@ import { StackLayoutBase } from './stack-layout-common'; import { CoreTypes } from '../../../core-types'; -import { View } from '../../core/view'; +import { Position, View } from '../../core/view'; import { layout } from '../../../utils'; import { Trace } from '../../../trace'; @@ -101,7 +101,7 @@ export class StackLayout extends StackLayoutBase { } } - private layoutVertical(left: number, top: number, right: number, bottom: number, insets: { left; top; right; bottom }): void { + private layoutVertical(left: number, top: number, right: number, bottom: number, insets: Position): void { const paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insets.left; const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top; const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight + insets.right; @@ -135,7 +135,7 @@ export class StackLayout extends StackLayoutBase { }); } - private layoutHorizontal(left: number, top: number, right: number, bottom: number, insets: { left; top; right; bottom }): void { + private layoutHorizontal(left: number, top: number, right: number, bottom: number, insets: Position): void { const paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insets.left; const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top; const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight + insets.right; diff --git a/packages/core/ui/styling/background.ios.ts b/packages/core/ui/styling/background.ios.ts index 5069286c03..ee51f655ed 100644 --- a/packages/core/ui/styling/background.ios.ts +++ b/packages/core/ui/styling/background.ios.ts @@ -1,9 +1,7 @@ import { ScrollEventData } from '../scroll-view'; -import { CoreTypes } from '../../core-types'; import { Background as BackgroundDefinition } from './background'; -import { View, Point } from '../core/view'; +import { View, Point, Position } from '../core/view'; import { LinearGradient } from './linear-gradient'; -import { Color } from '../../color'; import { Screen } from '../../platform'; import { isDataURI, isFileOrResourcePath, layout } from '../../utils'; import { ios as iosViewUtils, NativeScriptUIView } from '../utils'; @@ -15,13 +13,6 @@ import { BackgroundClearFlags } from './background-common'; export * from './background-common'; -interface Position { - top: number; - right: number; - bottom: number; - left: number; -} - interface BackgroundDrawParams { repeatX: boolean; repeatY: boolean; From 66f850240d3607147404d8010c32d8da505808b0 Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Wed, 18 Dec 2024 19:52:00 +0200 Subject: [PATCH 3/4] chore: More types --- packages/core/ui/core/view/index.d.ts | 2 +- packages/core/ui/core/view/index.ios.ts | 2 +- packages/core/ui/core/view/view-common.ts | 2 +- packages/core/ui/core/view/view-helper/index.d.ts | 6 +++--- packages/core/ui/core/view/view-helper/index.ios.ts | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/core/ui/core/view/index.d.ts b/packages/core/ui/core/view/index.d.ts index a9c58237e5..67ff792532 100644 --- a/packages/core/ui/core/view/index.d.ts +++ b/packages/core/ui/core/view/index.d.ts @@ -668,7 +668,7 @@ export abstract class View extends ViewCommon { /** * Returns the iOS safe area insets of this view. */ - public getSafeAreaInsets(): { left; top; right; bottom }; + public getSafeAreaInsets(): Position; /** * Returns the location of this view in the window coordinate system. diff --git a/packages/core/ui/core/view/index.ios.ts b/packages/core/ui/core/view/index.ios.ts index 028242a647..3272876f3a 100644 --- a/packages/core/ui/core/view/index.ios.ts +++ b/packages/core/ui/core/view/index.ios.ts @@ -327,7 +327,7 @@ export class View extends ViewCommon implements ViewDefinition { return null; } - public getSafeAreaInsets(): { left; top; right; bottom } { + public getSafeAreaInsets(): Position { const safeAreaInsets = this.nativeViewProtected && this.nativeViewProtected.safeAreaInsets; const insets = { left: 0, top: 0, right: 0, bottom: 0 }; if (this.iosIgnoreSafeArea) { diff --git a/packages/core/ui/core/view/view-common.ts b/packages/core/ui/core/view/view-common.ts index e64f4734f9..00f387c65d 100644 --- a/packages/core/ui/core/view/view-common.ts +++ b/packages/core/ui/core/view/view-common.ts @@ -1087,7 +1087,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition { return undefined; } - public getSafeAreaInsets(): { left; top; right; bottom } { + public getSafeAreaInsets(): Position { return { left: 0, top: 0, right: 0, bottom: 0 }; } diff --git a/packages/core/ui/core/view/view-helper/index.d.ts b/packages/core/ui/core/view/view-helper/index.d.ts index e0f246f49e..e685f2af2f 100644 --- a/packages/core/ui/core/view/view-helper/index.d.ts +++ b/packages/core/ui/core/view/view-helper/index.d.ts @@ -1,4 +1,4 @@ -import { View } from '..'; +import { Position, View } from '..'; export class ViewHelper { /** @@ -60,8 +60,8 @@ export namespace IOSHelper { export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: View): void; export function updateConstraints(controller: any /* UIViewController */, owner: View): void; export function layoutView(controller: any /* UIViewController */, owner: View): void; - export function getPositionFromFrame(frame: any /* CGRect */): { left; top; right; bottom }; - export function getFrameFromPosition(position: { left; top; right; bottom }, insets?: { left; top; right; bottom }): any; /* CGRect */ + export function getPositionFromFrame(frame: any /* CGRect */): Position; + export function getFrameFromPosition(position: Position, insets?: Position): any; /* CGRect */ export function shrinkToSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */ export function expandBeyondSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */ export class UILayoutViewController { 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 a5057372f0..c862eeeae8 100644 --- a/packages/core/ui/core/view/view-helper/index.ios.ts +++ b/packages/core/ui/core/view/view-helper/index.ios.ts @@ -1,5 +1,5 @@ // Types -import { View } from '..'; +import { Position, View } from '..'; // Requires import { ViewHelper } from './view-helper-common'; @@ -247,7 +247,7 @@ export class IOSHelper { } } - static getPositionFromFrame(frame: CGRect): { left; top; right; bottom } { + static getPositionFromFrame(frame: CGRect): Position { const left = layout.round(layout.toDevicePixels(frame.origin.x)); const top = layout.round(layout.toDevicePixels(frame.origin.y)); const right = layout.round(layout.toDevicePixels(frame.origin.x + frame.size.width)); @@ -256,7 +256,7 @@ export class IOSHelper { return { left, right, top, bottom }; } - static getFrameFromPosition(position: { left; top; right; bottom }, insets?: { left; top; right; bottom }): CGRect { + static getFrameFromPosition(position: Position, insets?: Position): CGRect { insets = insets || { left: 0, top: 0, right: 0, bottom: 0 }; const left = layout.toDeviceIndependentPixels(position.left + insets.left); From 1690e057604b3bfd705c60b7a3d25447ac7fc798 Mon Sep 17 00:00:00 2001 From: Dimitris - Rafail Katsampas Date: Wed, 18 Dec 2024 19:59:00 +0200 Subject: [PATCH 4/4] chore: Improved position change check --- packages/core/ui/core/view/index.ios.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/core/ui/core/view/index.ios.ts b/packages/core/ui/core/view/index.ios.ts index 3272876f3a..c041aaf7b6 100644 --- a/packages/core/ui/core/view/index.ios.ts +++ b/packages/core/ui/core/view/index.ios.ts @@ -124,11 +124,12 @@ export class View extends ViewCommon implements ViewDefinition { // get the frame and adjust the position, so that onLayout works correctly position = IOSHelper.getPositionFromFrame(this.nativeViewProtected.frame); - // If frame has actually changed, there is the need to update view background and border styles as they depend on native view bounds - // To trigger the needed visual update, mark size as changed - const positionChanged = position.left !== left || position.top !== top || position.right !== right || position.bottom !== bottom; - if (positionChanged && !sizeChanged) { - sizeChanged = true; + if (!sizeChanged) { + // If frame has actually changed, there is the need to update view background and border styles as they depend on native view bounds + // To trigger the needed visual update, mark size as changed + if (position.left !== left || position.top !== top || position.right !== right || position.bottom !== bottom) { + sizeChanged = true; + } } } else { position = { left, top, right, bottom };