fix(core): improve loaded/unloaded handling to be stable and consistent (#10170)

This commit is contained in:
Nathan Walker
2023-01-15 19:49:28 -08:00
committed by GitHub
parent a69a9d6921
commit c9e29aa9af
12 changed files with 121 additions and 166 deletions

View File

@ -291,22 +291,24 @@ export class Observable implements ObservableDefinition {
}
for (let i = observers.length - 1; i >= 0; i--) {
const entry = observers[i];
if (entry.once) {
observers.splice(i, 1);
}
if (entry) {
if (entry.once) {
observers.splice(i, 1);
}
let returnValue;
if (entry.thisArg) {
returnValue = entry.callback.apply(entry.thisArg, [data]);
} else {
returnValue = entry.callback(data);
}
let returnValue;
if (entry.thisArg) {
returnValue = entry.callback.apply(entry.thisArg, [data]);
} else {
returnValue = entry.callback(data);
}
// This ensures errors thrown inside asynchronous functions do not get swallowed
if (returnValue && returnValue instanceof Promise) {
returnValue.catch((err) => {
console.error(err);
});
// This ensures errors thrown inside asynchronous functions do not get swallowed
if (returnValue && returnValue instanceof Promise) {
returnValue.catch((err) => {
console.error(err);
});
}
}
}
}

View File

@ -21,9 +21,8 @@ export class Button extends ButtonBase {
public initNativeView(): void {
super.initNativeView();
const nativeView = this.nativeViewProtected;
this._tapHandler = TapHandlerImpl.initWithOwner(new WeakRef(this));
nativeView.addTargetActionForControlEvents(this._tapHandler, 'tap', UIControlEvents.TouchUpInside);
this.nativeViewProtected.addTargetActionForControlEvents(this._tapHandler, 'tap', UIControlEvents.TouchUpInside);
}
public disposeNativeView(): void {

View File

@ -430,14 +430,6 @@ export class View extends ViewCommon {
@profile
public onUnloaded() {
if (this.touchListenerIsSet) {
this.touchListenerIsSet = false;
if (this.nativeViewProtected) {
this.nativeViewProtected.setOnTouchListener(null);
this.nativeViewProtected.setClickable(this._isClickable);
}
}
this._manager = null;
this._rootManager = null;
super.onUnloaded();
@ -474,7 +466,6 @@ export class View extends ViewCommon {
public initNativeView(): void {
super.initNativeView();
this._isClickable = this.nativeViewProtected.isClickable();
if (this.needsOnLayoutChangeListener()) {
this.setOnLayoutChangeListener();
}
@ -486,7 +477,12 @@ export class View extends ViewCommon {
public disposeNativeView(): void {
super.disposeNativeView();
if (this.touchListenerIsSet) {
this.touchListenerIsSet = false;
if (this.nativeViewProtected) {
this.nativeViewProtected.setOnTouchListener(null);
}
}
if (this.layoutChangeListenerIsSet) {
this.layoutChangeListenerIsSet = false;
this.nativeViewProtected.removeOnLayoutChangeListener(this.layoutChangeListener);
@ -494,7 +490,7 @@ export class View extends ViewCommon {
}
setOnTouchListener() {
if (!this.nativeViewProtected || !this.hasGestureObservers()) {
if (this.touchListenerIsSet || !this.nativeViewProtected || !this.hasGestureObservers()) {
return;
}

View File

@ -479,12 +479,14 @@ export class View extends ViewCommon implements ViewDefinition {
} else {
//use CSS & attribute width & height if option is not provided
const handler = () => {
const w = <number>(this.width || this.style.width);
const h = <number>(this.height || this.style.height);
if (controller) {
const w = <number>(this.width || this.style.width);
const h = <number>(this.height || this.style.height);
//TODO: only numeric value is supported, percentage value is not supported like Android
if (w > 0 && h > 0) {
controller.preferredContentSize = CGSizeMake(w, h);
//TODO: only numeric value is supported, percentage value is not supported like Android
if (w > 0 && h > 0) {
controller.preferredContentSize = CGSizeMake(w, h);
}
}
this.off(View.loadedEvent, handler);

View File

@ -17,9 +17,9 @@ export class ListPicker extends ListPickerBase {
initNativeView() {
super.initNativeView();
const nativeView = this.nativeViewProtected;
nativeView.dataSource = this._dataSource = ListPickerDataSource.initWithOwner(new WeakRef(this));
this.nativeViewProtected.dataSource = this._dataSource = ListPickerDataSource.initWithOwner(new WeakRef(this));
this._delegate = ListPickerDelegateImpl.initWithOwner(new WeakRef(this));
this.nativeViewProtected.delegate = this._delegate;
}
public disposeNativeView() {
@ -33,17 +33,6 @@ export class ListPicker extends ListPickerBase {
return this.nativeViewProtected;
}
@profile
public onLoaded() {
super.onLoaded();
this.ios.delegate = this._delegate;
}
public onUnloaded() {
this.ios.delegate = null;
super.onUnloaded();
}
[selectedIndexProperty.getDefault](): number {
return -1;
}

View File

@ -118,17 +118,10 @@ export class ScrollView extends ScrollViewBase {
this.nativeViewProtected.setId(this._androidViewId);
}
public _onOrientationChanged() {
if (this.nativeViewProtected) {
const parent = this.parent;
if (parent) {
parent._removeView(this);
parent._addView(this);
}
protected addNativeListener() {
if (!this.nativeViewProtected) {
return;
}
}
protected attachNative() {
const that = new WeakRef(this);
if (this.orientation === 'vertical') {
this.scrollChangeHandler = new androidx.core.widget.NestedScrollView.OnScrollChangeListener({
@ -144,7 +137,7 @@ export class ScrollView extends ScrollViewBase {
}
},
});
this.nativeView.setOnScrollChangeListener(this.scrollChangeHandler);
this.nativeViewProtected.setOnScrollChangeListener(this.scrollChangeHandler);
} else {
this.handler = new android.view.ViewTreeObserver.OnScrollChangedListener({
onScrollChanged: function () {
@ -158,6 +151,35 @@ export class ScrollView extends ScrollViewBase {
}
}
protected removeNativeListener() {
if (!this.nativeViewProtected) {
return;
}
if (this.handler) {
this.nativeViewProtected?.getViewTreeObserver().removeOnScrollChangedListener(this.handler);
this.handler = null;
}
if (this.scrollChangeHandler) {
this.nativeView?.setOnScrollChangeListener(null);
this.scrollChangeHandler = null;
}
}
disposeNativeView() {
super.disposeNativeView();
this.removeNativeListener();
}
public _onOrientationChanged() {
if (this.nativeViewProtected) {
const parent = this.parent;
if (parent) {
parent._removeView(this);
parent._addView(this);
}
}
}
private _lastScrollX = -1;
private _lastScrollY = -1;
private _onScrollChanged() {
@ -179,17 +201,6 @@ export class ScrollView extends ScrollViewBase {
}
}
}
protected dettachNative() {
if (this.handler) {
this.nativeViewProtected?.getViewTreeObserver().removeOnScrollChangedListener(this.handler);
this.handler = null;
}
if (this.scrollChangeHandler) {
this.nativeView?.setOnScrollChangeListener(null);
this.scrollChangeHandler = null;
}
}
}
ScrollView.prototype.recycleNativeView = 'never';

View File

@ -40,9 +40,7 @@ export class ScrollView extends ScrollViewBase {
private _delegate: UIScrollViewDelegateImpl;
public createNativeView() {
const view = UIScrollView.new();
return view;
return UIScrollView.new();
}
initNativeView() {
@ -51,20 +49,34 @@ export class ScrollView extends ScrollViewBase {
this._setNativeClipToBounds();
}
_setNativeClipToBounds() {
// Always set clipsToBounds for scroll-view
this.nativeViewProtected.clipsToBounds = true;
disposeNativeView() {
this._delegate = null;
super.disposeNativeView();
}
protected attachNative() {
protected addNativeListener() {
if (!this.nativeViewProtected) {
return;
}
this._delegate = UIScrollViewDelegateImpl.initWithOwner(new WeakRef(this));
this.nativeViewProtected.delegate = this._delegate;
}
protected dettachNative() {
protected removeNativeListener() {
if (!this.nativeViewProtected) {
return;
}
this.nativeViewProtected.delegate = null;
}
_setNativeClipToBounds() {
if (!this.nativeViewProtected) {
return;
}
// Always set clipsToBounds for scroll-view
this.nativeViewProtected.clipsToBounds = true;
}
protected updateScrollBarVisibility(value) {
if (!this.nativeViewProtected) {
return;

View File

@ -9,7 +9,7 @@ import { CoreTypes } from '../../core-types';
@CSSType('ScrollView')
export abstract class ScrollViewBase extends ContentView implements ScrollViewDefinition {
private _scrollChangeCount = 0;
private _addedScrollEvent = false;
public static scrollEvent = 'scroll';
public orientation: CoreTypes.OrientationType;
@ -19,52 +19,27 @@ export abstract class ScrollViewBase extends ContentView implements ScrollViewDe
public addEventListener(arg: string, callback: any, thisArg?: any) {
super.addEventListener(arg, callback, thisArg);
if (arg === ScrollViewBase.scrollEvent) {
this._scrollChangeCount++;
this.attach();
if (arg === ScrollViewBase.scrollEvent && !this._addedScrollEvent) {
this._addedScrollEvent = true;
this.addNativeListener();
}
}
public removeEventListener(arg: string, callback: any, thisArg?: any) {
super.removeEventListener(arg, callback, thisArg);
if (arg === ScrollViewBase.scrollEvent) {
this._scrollChangeCount--;
this.dettach();
if (arg === ScrollViewBase.scrollEvent && this._addedScrollEvent) {
this._addedScrollEvent = false;
this.removeNativeListener();
}
}
@profile
public onLoaded() {
super.onLoaded();
this.attach();
protected addNativeListener() {
// implemented per platform
}
public onUnloaded() {
super.onUnloaded();
this.dettach();
}
private attach() {
if (this._scrollChangeCount > 0 && this.isLoaded) {
this.attachNative();
}
}
private dettach() {
if (this._scrollChangeCount === 0 && this.isLoaded) {
this.dettachNative();
}
}
protected attachNative() {
//
}
protected dettachNative() {
//
protected removeNativeListener() {
// implemented per platform
}
get horizontalOffset(): number {

View File

@ -82,6 +82,7 @@ export class SearchBar extends SearchBarBase {
initNativeView() {
super.initNativeView();
this._delegate = UISearchBarDelegateImpl.initWithOwner(new WeakRef(this));
this.nativeViewProtected.delegate = this._delegate;
}
disposeNativeView() {
@ -89,16 +90,6 @@ export class SearchBar extends SearchBarBase {
super.disposeNativeView();
}
public onLoaded() {
super.onLoaded();
this.ios.delegate = this._delegate;
}
public onUnloaded() {
this.ios.delegate = null;
super.onUnloaded();
}
public dismissSoftInput() {
(<UIResponder>this.ios).resignFirstResponder();
}

View File

@ -123,6 +123,7 @@ export class TextField extends TextFieldBase {
initNativeView() {
super.initNativeView();
this._delegate = UITextFieldDelegateImpl.initWithOwner(new WeakRef(this));
this.nativeViewProtected.delegate = this._delegate;
}
disposeNativeView() {
@ -130,17 +131,6 @@ export class TextField extends TextFieldBase {
super.disposeNativeView();
}
@profile
public onLoaded() {
super.onLoaded();
this.ios.delegate = this._delegate;
}
public onUnloaded() {
this.ios.delegate = null;
super.onUnloaded();
}
// @ts-ignore
get ios(): UITextField {
return this.nativeViewProtected;

View File

@ -106,6 +106,7 @@ export class TextView extends TextViewBaseCommon {
initNativeView() {
super.initNativeView();
this._delegate = UITextViewDelegateImpl.initWithOwner(new WeakRef(this));
this.nativeTextViewProtected.delegate = this._delegate;
}
disposeNativeView() {
@ -113,17 +114,6 @@ export class TextView extends TextViewBaseCommon {
super.disposeNativeView();
}
@profile
public onLoaded() {
super.onLoaded();
this.nativeTextViewProtected.delegate = this._delegate;
}
public onUnloaded() {
this.nativeTextViewProtected.delegate = null;
super.onUnloaded();
}
// @ts-ignore
get ios(): UITextView {
return this.nativeViewProtected;

View File

@ -202,18 +202,16 @@ export class WebView extends WebViewBase {
this._delegate = WKNavigationDelegateImpl.initWithOwner(new WeakRef(this));
this._scrollDelegate = UIScrollViewDelegateImpl.initWithOwner(new WeakRef(this));
this._uiDelegate = WKUIDelegateImpl.initWithOwner(new WeakRef(this));
this.ios.navigationDelegate = this._delegate;
this.ios.scrollView.delegate = this._scrollDelegate;
this.ios.UIDelegate = this._uiDelegate;
this.nativeViewProtected.navigationDelegate = this._delegate;
this.nativeViewProtected.scrollView.delegate = this._scrollDelegate;
this.nativeViewProtected.UIDelegate = this._uiDelegate;
}
@profile
public onLoaded() {
super.onLoaded();
}
public onUnloaded() {
super.onUnloaded();
disposeNativeView() {
super.disposeNativeView();
this._delegate = null;
this._scrollDelegate = null;
this._uiDelegate = null;
}
// @ts-ignore
@ -222,48 +220,48 @@ export class WebView extends WebViewBase {
}
public stopLoading() {
this.ios.stopLoading();
this.nativeViewProtected.stopLoading();
}
public _loadUrl(src: string) {
if (src.startsWith('file:///')) {
const cachePath = src.substring(0, src.lastIndexOf('/'));
this.ios.loadFileURLAllowingReadAccessToURL(NSURL.URLWithString(src), NSURL.URLWithString(cachePath));
this.nativeViewProtected.loadFileURLAllowingReadAccessToURL(NSURL.URLWithString(src), NSURL.URLWithString(cachePath));
} else {
this.ios.loadRequest(NSURLRequest.requestWithURL(NSURL.URLWithString(src)));
this.nativeViewProtected.loadRequest(NSURLRequest.requestWithURL(NSURL.URLWithString(src)));
}
}
public _loadData(content: string) {
this.ios.loadHTMLStringBaseURL(content, NSURL.alloc().initWithString(`file:///${knownFolders.currentApp().path}/`));
this.nativeViewProtected.loadHTMLStringBaseURL(content, NSURL.alloc().initWithString(`file:///${knownFolders.currentApp().path}/`));
}
get canGoBack(): boolean {
return this.ios.canGoBack;
return this.nativeViewProtected.canGoBack;
}
get canGoForward(): boolean {
return this.ios.canGoForward;
return this.nativeViewProtected.canGoForward;
}
public goBack() {
this.ios.goBack();
this.nativeViewProtected.goBack();
}
public goForward() {
this.ios.goForward();
this.nativeViewProtected.goForward();
}
public reload() {
this.ios.reload();
this.nativeViewProtected.reload();
}
[disableZoomProperty.setNative](value: boolean) {
if (!value && typeof this._minimumZoomScale === 'number' && typeof this._maximumZoomScale === 'number' && typeof this._zoomScale === 'number') {
if (this.ios.scrollView) {
this.ios.scrollView.minimumZoomScale = this._minimumZoomScale;
this.ios.scrollView.maximumZoomScale = this._maximumZoomScale;
this.ios.scrollView.zoomScale = this._zoomScale;
if (this.nativeViewProtected?.scrollView) {
this.nativeViewProtected.scrollView.minimumZoomScale = this._minimumZoomScale;
this.nativeViewProtected.scrollView.maximumZoomScale = this._maximumZoomScale;
this.nativeViewProtected.scrollView.zoomScale = this._zoomScale;
this._minimumZoomScale = undefined;
this._maximumZoomScale = undefined;
this._zoomScale = undefined;