fix(ios): proper disposal and recreation of iOS native views (#9879)

This commit is contained in:
Dimitris - Rafail Katsampas
2022-04-30 21:00:30 +03:00
committed by Nathan Walker
parent b7e6128576
commit f548fdc735
5 changed files with 36 additions and 19 deletions

View File

@ -1,7 +1,7 @@
import * as helper from '../../ui-helper'; import * as helper from '../../ui-helper';
import * as btnCounter from './pages/button-counter'; import * as btnCounter from './pages/button-counter';
import * as TKUnit from '../../tk-unit'; import * as TKUnit from '../../tk-unit';
import { isIOS, isAndroid } from '@nativescript/core'; import { isIOS } from '@nativescript/core';
// Integration tests that asser sertain runtime behavior, lifecycle events atc. // Integration tests that asser sertain runtime behavior, lifecycle events atc.
@ -163,7 +163,7 @@ export function test_css_sets_properties() {
page.content = stack; page.content = stack;
// TODO: The check counts here should be the same as the counts before removing from the page. // TODO: The check counts here should be the same as the counts before removing from the page.
const expectedNativeSettersAfterReaddedToPage = isAndroid ? [2, 4, 4, 4] : expectedChangesAfterResettingClasses; const expectedNativeSettersAfterReaddedToPage = [2, 4, 4, 4];
for (let i = 0; i < buttons.length; i++) { for (let i = 0; i < buttons.length; i++) {
TKUnit.assertEqual(buttons[i].colorSetNativeCount, expectedNativeSettersAfterReaddedToPage[i], `Expected ${buttons[i].id} native set to not be called when added to page.`); TKUnit.assertEqual(buttons[i].colorSetNativeCount, expectedNativeSettersAfterReaddedToPage[i], `Expected ${buttons[i].id} native set to not be called when added to page.`);
TKUnit.assertEqual(buttons[i].colorPropertyChangeCount, expectedChangesAfterResettingClasses[i], `Expected ${buttons[i].id} change notifications for css properties to not occur when added to page.`); TKUnit.assertEqual(buttons[i].colorPropertyChangeCount, expectedChangesAfterResettingClasses[i], `Expected ${buttons[i].id} change notifications for css properties to not occur when added to page.`);

View File

@ -69,5 +69,5 @@ export interface AnimationDefinitionInternal extends AnimationDefinition {
export interface IOSView extends View { export interface IOSView extends View {
_suspendPresentationLayerUpdates(); _suspendPresentationLayerUpdates();
_resumePresentationLayerUpdates(); _resumePresentationLayerUpdates();
_isPresentationLayerUpdateSuspeneded(); _isPresentationLayerUpdateSuspended();
} }

View File

@ -909,6 +909,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
public destroyNode(forceDestroyChildren?: boolean): void { public destroyNode(forceDestroyChildren?: boolean): void {
this.reusable = false; this.reusable = false;
this.callUnloaded();
this._tearDownUI(forceDestroyChildren); this._tearDownUI(forceDestroyChildren);
} }
@ -958,12 +959,9 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
this._suspendNativeUpdates(SuspendType.UISetup); this._suspendNativeUpdates(SuspendType.UISetup);
if (global.isAndroid) { this.setNativeView(null);
this.setNativeView(null); this._androidView = null;
this._androidView = null; this._iosView = null;
}
// this._iosView = null;
this._context = null; this._context = null;
} }

View File

@ -37,7 +37,8 @@ export class View extends ViewCommon implements ViewDefinition {
*/ */
private _modalAnimatedOptions: Array<boolean>; private _modalAnimatedOptions: Array<boolean>;
private _isLaidOut = false; private _isLaidOut = false;
private _hasTransfrom = false; private _hasTransform = false;
private _hasPendingTransform = false;
private _privateFlags: number = PFLAG_LAYOUT_REQUIRED | PFLAG_FORCE_LAYOUT; private _privateFlags: number = PFLAG_LAYOUT_REQUIRED | PFLAG_FORCE_LAYOUT;
private _cachedFrame: CGRect; private _cachedFrame: CGRect;
private _suspendCATransaction = false; private _suspendCATransaction = false;
@ -63,6 +64,15 @@ export class View extends ViewCommon implements ViewDefinition {
this.once(View.loadedEvent, () => setupAccessibleView(this)); this.once(View.loadedEvent, () => setupAccessibleView(this));
} }
disposeNativeView() {
super.disposeNativeView();
this._cachedFrame = null;
this._isLaidOut = false;
this._hasTransform = false;
this._hasPendingTransform = false;
}
public requestLayout(): void { public requestLayout(): void {
super.requestLayout(); super.requestLayout();
this._privateFlags |= PFLAG_FORCE_LAYOUT; this._privateFlags |= PFLAG_FORCE_LAYOUT;
@ -119,6 +129,10 @@ export class View extends ViewCommon implements ViewDefinition {
} }
this.updateBackground(sizeChanged); this.updateBackground(sizeChanged);
if (this._hasPendingTransform) {
this.updateNativeTransform();
this._hasPendingTransform = false;
}
this._privateFlags &= ~PFLAG_FORCE_LAYOUT; this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
} }
@ -175,7 +189,7 @@ export class View extends ViewCommon implements ViewDefinition {
this._cachedFrame = frame; this._cachedFrame = frame;
let adjustedFrame = null; let adjustedFrame = null;
let transform = null; let transform = null;
if (this._hasTransfrom) { if (this._hasTransform) {
// Always set identity transform before setting frame; // Always set identity transform before setting frame;
transform = nativeView.layer.transform; transform = nativeView.layer.transform;
nativeView.layer.transform = CATransform3DIdentity; nativeView.layer.transform = CATransform3DIdentity;
@ -189,7 +203,7 @@ export class View extends ViewCommon implements ViewDefinition {
nativeView.frame = adjustedFrame; nativeView.frame = adjustedFrame;
} }
if (this._hasTransfrom) { if (this._hasTransform) {
// re-apply the transform after the frame is adjusted // re-apply the transform after the frame is adjusted
nativeView.layer.transform = transform; nativeView.layer.transform = transform;
} }
@ -363,6 +377,11 @@ export class View extends ViewCommon implements ViewDefinition {
} }
public updateNativeTransform() { public updateNativeTransform() {
if (!this.isLayoutValid) {
this._hasPendingTransform = true;
return;
}
const scaleX = this.scaleX || 1e-6; const scaleX = this.scaleX || 1e-6;
const scaleY = this.scaleY || 1e-6; const scaleY = this.scaleY || 1e-6;
const perspective = this.perspective || 300; const perspective = this.perspective || 300;
@ -378,12 +397,12 @@ export class View extends ViewCommon implements ViewDefinition {
transform = iOSNativeHelper.applyRotateTransform(transform, this.rotateX, this.rotateY, this.rotate); transform = iOSNativeHelper.applyRotateTransform(transform, this.rotateX, this.rotateY, this.rotate);
transform = CATransform3DScale(transform, scaleX, scaleY, 1); transform = CATransform3DScale(transform, scaleX, scaleY, 1);
if (!CATransform3DEqualToTransform(this.nativeViewProtected.layer.transform, transform)) { if (!CATransform3DEqualToTransform(this.nativeViewProtected.layer.transform, transform)) {
const updateSuspended = this._isPresentationLayerUpdateSuspeneded(); const updateSuspended = this._isPresentationLayerUpdateSuspended();
if (!updateSuspended) { if (!updateSuspended) {
CATransaction.begin(); CATransaction.begin();
} }
this.nativeViewProtected.layer.transform = transform; this.nativeViewProtected.layer.transform = transform;
this._hasTransfrom = this.nativeViewProtected && !CATransform3DEqualToTransform(this.nativeViewProtected.transform3D, CATransform3DIdentity); this._hasTransform = this.nativeViewProtected && !CATransform3DEqualToTransform(this.nativeViewProtected.transform3D, CATransform3DIdentity);
if (!updateSuspended) { if (!updateSuspended) {
CATransaction.commit(); CATransaction.commit();
} }
@ -409,7 +428,7 @@ export class View extends ViewCommon implements ViewDefinition {
this._suspendCATransaction = false; this._suspendCATransaction = false;
} }
public _isPresentationLayerUpdateSuspeneded(): boolean { public _isPresentationLayerUpdateSuspended(): boolean {
return this._suspendCATransaction || this._suspendNativeUpdatesCount > 0; return this._suspendCATransaction || this._suspendNativeUpdatesCount > 0;
} }
@ -689,7 +708,7 @@ export class View extends ViewCommon implements ViewDefinition {
} }
[opacityProperty.setNative](value: number) { [opacityProperty.setNative](value: number) {
const nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;
const updateSuspended = this._isPresentationLayerUpdateSuspeneded(); const updateSuspended = this._isPresentationLayerUpdateSuspended();
if (!updateSuspended) { if (!updateSuspended) {
CATransaction.begin(); CATransaction.begin();
} }
@ -845,7 +864,7 @@ export class View extends ViewCommon implements ViewDefinition {
} }
_redrawNativeBackground(value: UIColor | Background): void { _redrawNativeBackground(value: UIColor | Background): void {
const updateSuspended = this._isPresentationLayerUpdateSuspeneded(); const updateSuspended = this._isPresentationLayerUpdateSuspended();
if (!updateSuspended) { if (!updateSuspended) {
CATransaction.begin(); CATransaction.begin();
} }

View File

@ -101,7 +101,7 @@ class UILayoutViewController extends UIViewController {
IOSHelper.updateAutoAdjustScrollInsets(this, owner); IOSHelper.updateAutoAdjustScrollInsets(this, owner);
if (!owner.parent) { if (!owner.isLoaded && !owner.parent) {
owner.callLoaded(); owner.callLoaded();
} }
} }
@ -109,7 +109,7 @@ class UILayoutViewController extends UIViewController {
public viewDidDisappear(animated: boolean): void { public viewDidDisappear(animated: boolean): void {
super.viewDidDisappear(animated); super.viewDidDisappear(animated);
const owner = this.owner.get(); const owner = this.owner.get();
if (owner && !owner.parent) { if (owner && owner.isLoaded && !owner.parent) {
owner.callUnloaded(); owner.callUnloaded();
} }
} }