fix(ios): background styles after frame changed by safe area (#10661)

This commit is contained in:
Dimitris-Rafail Katsampas
2025-01-13 05:51:33 +02:00
committed by GitHub
parent 87742638d1
commit 1e86ed9da8
9 changed files with 47 additions and 57 deletions

View File

@ -1,5 +1,5 @@
// Definitions. // Definitions.
import type { Point, CustomLayoutView as CustomLayoutViewDefinition } from '.'; import type { Point, CustomLayoutView as CustomLayoutViewDefinition, Position } from '.';
import type { GestureTypes, GestureEventData } from '../../gestures'; import type { GestureTypes, GestureEventData } from '../../gestures';
// Types. // Types.
@ -577,12 +577,7 @@ export class View extends ViewCommon {
} }
} }
_getCurrentLayoutBounds(): { _getCurrentLayoutBounds(): Position {
left: number;
top: number;
right: number;
bottom: number;
} {
if (this.nativeViewProtected && !this.isCollapsed) { if (this.nativeViewProtected && !this.isCollapsed) {
return { return {
left: this.nativeViewProtected.getLeft(), left: this.nativeViewProtected.getLeft(),

View File

@ -64,6 +64,13 @@ export interface Point {
z?: number; z?: number;
} }
export interface Position {
top: number;
right: number;
bottom: number;
left: number;
}
/** /**
* The Size interface describes abstract dimensions in two dimensional space. * 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. * It has two properties width and height, representing the width and height values of the size.
@ -857,7 +864,7 @@ export abstract class View extends ViewCommon {
/** /**
* Returns the iOS safe area insets of this view. * 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. * Returns the location of this view in the window coordinate system.
@ -1013,12 +1020,7 @@ export abstract class View extends ViewCommon {
* Return view bounds. * Return view bounds.
* @private * @private
*/ */
_getCurrentLayoutBounds(): { _getCurrentLayoutBounds(): Position;
left: number;
top: number;
right: number;
bottom: number;
};
/** /**
* @private * @private
*/ */

View File

@ -1,5 +1,5 @@
// Types. // Types.
import { Point, View as ViewDefinition } from '.'; import { Point, Position, View as ViewDefinition } from '.';
// Requires // Requires
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common'; import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
@ -108,19 +108,31 @@ export class View extends ViewCommon implements ViewDefinition {
@profile @profile
public layout(left: number, top: number, right: number, bottom: number, setFrame = true): void { 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) { if (setFrame) {
this.layoutNativeView(left, top, right, bottom); 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) { if (needsLayout) {
let position = { left, top, right, bottom }; let position: Position;
if (this.nativeViewProtected && SDK_VERSION > 10) { if (this.nativeViewProtected && SDK_VERSION > 10) {
// on iOS 11+ it is possible to have a changed layout frame due to safe area insets // 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 // get the frame and adjust the position, so that onLayout works correctly
const frame = this.nativeViewProtected.frame; position = IOSHelper.getPositionFromFrame(this.nativeViewProtected.frame);
position = IOSHelper.getPositionFromFrame(frame);
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 };
} }
this.onLayout(position.left, position.top, position.right, position.bottom); this.onLayout(position.left, position.top, position.right, position.bottom);
@ -316,7 +328,7 @@ export class View extends ViewCommon implements ViewDefinition {
return null; return null;
} }
public getSafeAreaInsets(): { left; top; right; bottom } { public getSafeAreaInsets(): Position {
const safeAreaInsets = this.nativeViewProtected && this.nativeViewProtected.safeAreaInsets; const safeAreaInsets = this.nativeViewProtected && this.nativeViewProtected.safeAreaInsets;
const insets = { left: 0, top: 0, right: 0, bottom: 0 }; const insets = { left: 0, top: 0, right: 0, bottom: 0 };
if (this.iosIgnoreSafeArea) { if (this.iosIgnoreSafeArea) {
@ -938,12 +950,7 @@ export class View extends ViewCommon implements ViewDefinition {
}); });
} }
_getCurrentLayoutBounds(): { _getCurrentLayoutBounds(): Position {
left: number;
top: number;
right: number;
bottom: number;
} {
const nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;
if (nativeView && !this.isCollapsed) { if (nativeView && !this.isCollapsed) {
const frame = nativeView.frame; const frame = nativeView.frame;

View File

@ -1,5 +1,5 @@
// Definitions. // 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 { booleanConverter, ShowModalOptions, ViewBase } from '../view-base';
import { getEventOrGestureName } from '../bindable'; import { getEventOrGestureName } from '../bindable';
@ -1066,12 +1066,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return changed; return changed;
} }
_getCurrentLayoutBounds(): { _getCurrentLayoutBounds(): Position {
left: number;
top: number;
right: number;
bottom: number;
} {
return { left: 0, top: 0, right: 0, bottom: 0 }; return { left: 0, top: 0, right: 0, bottom: 0 };
} }
@ -1110,7 +1105,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return undefined; return undefined;
} }
public getSafeAreaInsets(): { left; top; right; bottom } { public getSafeAreaInsets(): Position {
return { left: 0, top: 0, right: 0, bottom: 0 }; return { left: 0, top: 0, right: 0, bottom: 0 };
} }

View File

@ -1,4 +1,4 @@
import { View } from '..'; import { Position, View } from '..';
export class ViewHelper { export class ViewHelper {
/** /**
@ -60,8 +60,8 @@ export namespace IOSHelper {
export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: View): void; export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: View): void;
export function updateConstraints(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 layoutView(controller: any /* UIViewController */, owner: View): void;
export function getPositionFromFrame(frame: any /* CGRect */): { left; top; right; bottom }; export function getPositionFromFrame(frame: any /* CGRect */): Position;
export function getFrameFromPosition(position: { left; top; right; bottom }, insets?: { left; top; right; bottom }): any; /* CGRect */ export function getFrameFromPosition(position: Position, insets?: Position): any; /* CGRect */
export function shrinkToSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */ export function shrinkToSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */
export function expandBeyondSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */ export function expandBeyondSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */
export class UILayoutViewController { export class UILayoutViewController {

View File

@ -1,5 +1,5 @@
// Types // Types
import { View } from '..'; import { Position, View } from '..';
// Requires // Requires
import { ViewHelper } from './view-helper-common'; 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 left = layout.round(layout.toDevicePixels(frame.origin.x));
const top = layout.round(layout.toDevicePixels(frame.origin.y)); const top = layout.round(layout.toDevicePixels(frame.origin.y));
const right = layout.round(layout.toDevicePixels(frame.origin.x + frame.size.width)); const right = layout.round(layout.toDevicePixels(frame.origin.x + frame.size.width));
@ -256,7 +256,7 @@ export class IOSHelper {
return { left, right, top, bottom }; 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 }; insets = insets || { left: 0, top: 0, right: 0, bottom: 0 };
const left = layout.toDeviceIndependentPixels(position.left + insets.left); const left = layout.toDeviceIndependentPixels(position.left + insets.left);

View File

@ -1,5 +1,5 @@
import { FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, FlexboxLayoutBase, FlexBasisPercent, orderProperty, flexGrowProperty, flexShrinkProperty, flexWrapBeforeProperty, alignSelfProperty } from './flexbox-layout-common'; 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'; import { layout } from '../../../utils';
export * from './flexbox-layout-common'; 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 // include insets
const paddingLeft = this.effectivePaddingLeft + insets.left; const paddingLeft = this.effectivePaddingLeft + insets.left;
const paddingTop = this.effectivePaddingTop + insets.top; 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 paddingLeft = this.effectivePaddingLeft + insets.left;
const paddingTop = this.effectivePaddingTop + insets.top; const paddingTop = this.effectivePaddingTop + insets.top;
const paddingRight = this.effectivePaddingRight + insets.right; const paddingRight = this.effectivePaddingRight + insets.right;

View File

@ -1,6 +1,6 @@
import { StackLayoutBase } from './stack-layout-common'; import { StackLayoutBase } from './stack-layout-common';
import { CoreTypes } from '../../../core-types'; import { CoreTypes } from '../../../core-types';
import { View } from '../../core/view'; import { Position, View } from '../../core/view';
import { layout } from '../../../utils'; import { layout } from '../../../utils';
import { Trace } from '../../../trace'; 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 paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insets.left;
const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top; const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top;
const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight + insets.right; 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 paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insets.left;
const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top; const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top;
const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight + insets.right; const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight + insets.right;

View File

@ -1,9 +1,7 @@
import { ScrollEventData } from '../scroll-view'; import { ScrollEventData } from '../scroll-view';
import { CoreTypes } from '../../core-types';
import { Background as BackgroundDefinition } from './background'; 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 { LinearGradient } from './linear-gradient';
import { Color } from '../../color';
import { Screen } from '../../platform'; import { Screen } from '../../platform';
import { isDataURI, isFileOrResourcePath, layout } from '../../utils'; import { isDataURI, isFileOrResourcePath, layout } from '../../utils';
import { ios as iosViewUtils, NativeScriptUIView } from '../utils'; import { ios as iosViewUtils, NativeScriptUIView } from '../utils';
@ -16,13 +14,6 @@ import { BackgroundClearFlags } from './background-common';
export * from './background-common'; export * from './background-common';
interface Position {
top: number;
right: number;
bottom: number;
left: number;
}
interface BackgroundDrawParams { interface BackgroundDrawParams {
repeatX: boolean; repeatX: boolean;
repeatY: boolean; repeatY: boolean;