mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 01:43:14 +08:00
fix(core): pseudo-class handlers failing to unsubscribe listeners (#10680)
This commit is contained in:

committed by
GitHub

parent
4902e27818
commit
e6beb1d816
@ -103,6 +103,7 @@ export class Button extends ButtonBase {
|
||||
this.on(GestureTypes[GestureTypes.touch], onButtonStateChange);
|
||||
} else {
|
||||
this.off(GestureTypes[GestureTypes.touch], onButtonStateChange);
|
||||
this._removeVisualState('highlighted');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,12 @@ export class Button extends ButtonBase {
|
||||
|
||||
public disposeNativeView(): void {
|
||||
this._tapHandler = null;
|
||||
|
||||
if (this._stateChangedHandler) {
|
||||
this._stateChangedHandler.stop();
|
||||
this._stateChangedHandler = null;
|
||||
}
|
||||
|
||||
super.disposeNativeView();
|
||||
}
|
||||
|
||||
@ -37,28 +43,32 @@ export class Button extends ButtonBase {
|
||||
return this.nativeViewProtected;
|
||||
}
|
||||
|
||||
public onUnloaded() {
|
||||
super.onUnloaded();
|
||||
if (this._stateChangedHandler) {
|
||||
this._stateChangedHandler.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@PseudoClassHandler('normal', 'highlighted', 'pressed', 'active')
|
||||
_updateButtonStateChangeHandler(subscribe: boolean) {
|
||||
if (subscribe) {
|
||||
if (!this._stateChangedHandler) {
|
||||
const viewRef = new WeakRef<Button>(this);
|
||||
|
||||
this._stateChangedHandler = new ControlStateChangeListener(this.nativeViewProtected, observableVisualStates, (state: string, add: boolean) => {
|
||||
const view = viewRef?.deref?.();
|
||||
|
||||
if (view) {
|
||||
if (add) {
|
||||
this._addVisualState(state);
|
||||
view._addVisualState(state);
|
||||
} else {
|
||||
this._removeVisualState(state);
|
||||
view._removeVisualState(state);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this._stateChangedHandler.start();
|
||||
} else {
|
||||
this._stateChangedHandler.stop();
|
||||
|
||||
// Remove any possible pseudo-class leftovers
|
||||
for (const state of observableVisualStates) {
|
||||
this._removeVisualState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
@NativeClass
|
||||
class ObserverClass extends NSObject {
|
||||
public callback: WeakRef<ControlStateChangeListenerCallback>;
|
||||
public callback: ControlStateChangeListenerCallback;
|
||||
|
||||
public static initWithCallback(callback: WeakRef<ControlStateChangeListenerCallback>): ObserverClass {
|
||||
public static initWithCallback(callback: ControlStateChangeListenerCallback): ObserverClass {
|
||||
const observer = <ObserverClass>ObserverClass.alloc().init();
|
||||
observer.callback = callback;
|
||||
|
||||
@ -12,8 +12,7 @@ class ObserverClass extends NSObject {
|
||||
}
|
||||
|
||||
public observeValueForKeyPathOfObjectChangeContext(path: string, object: UIControl) {
|
||||
const callback = this.callback?.deref();
|
||||
|
||||
const callback = this.callback;
|
||||
if (callback) {
|
||||
callback(path, object[path]);
|
||||
}
|
||||
@ -30,7 +29,7 @@ export class ControlStateChangeListener implements ControlStateChangeListenerDef
|
||||
constructor(control: UIControl, states: string[], callback: ControlStateChangeListenerCallback) {
|
||||
this._control = control;
|
||||
this._states = states;
|
||||
this._observer = ObserverClass.initWithCallback(new WeakRef(callback));
|
||||
this._observer = ObserverClass.initWithCallback(callback);
|
||||
}
|
||||
|
||||
public start() {
|
||||
|
@ -53,19 +53,21 @@ export function viewMatchesModuleContext(view: ViewDefinition, context: ModuleCo
|
||||
|
||||
export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator {
|
||||
const stateEventNames = pseudoClasses.map((s) => ':' + s);
|
||||
const listeners = Symbol('listeners');
|
||||
|
||||
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => {
|
||||
function update(change: number) {
|
||||
const prev = this[listeners] || 0;
|
||||
const next = prev + change;
|
||||
if (prev <= 0 && next > 0) {
|
||||
this[propertyKey](true);
|
||||
} else if (prev > 0 && next <= 0) {
|
||||
this[propertyKey](false);
|
||||
return <T>(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<T>) => {
|
||||
// This will help keep track of pseudo-class subscription changes
|
||||
const subscribeKey = Symbol(propertyKey + '_flag');
|
||||
|
||||
function onSubscribe(subscribe: boolean) {
|
||||
if (subscribe != !!this[subscribeKey]) {
|
||||
this[subscribeKey] = subscribe;
|
||||
this[propertyKey](subscribe);
|
||||
}
|
||||
}
|
||||
stateEventNames.forEach((s) => (target[s] = update));
|
||||
|
||||
for (const eventName of stateEventNames) {
|
||||
target[eventName] = onSubscribe;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -34,19 +34,25 @@ export abstract class EditableTextBase extends TextBase implements EditableTextB
|
||||
public autocorrect: boolean;
|
||||
public hint: string;
|
||||
public maxLength: number;
|
||||
public placeholderColor: Color;
|
||||
public valueFormatter: (value: string) => string;
|
||||
|
||||
public abstract dismissSoftInput();
|
||||
public abstract _setInputType(inputType: number): void;
|
||||
public abstract setSelection(start: number, stop?: number);
|
||||
placeholderColor: Color;
|
||||
|
||||
@PseudoClassHandler('focus', 'blur')
|
||||
_updateTextBaseFocusStateHandler(subscribe) {
|
||||
const method = subscribe ? 'on' : 'off';
|
||||
_updateTextBaseFocusStateHandler(subscribe: boolean) {
|
||||
if (subscribe) {
|
||||
this.on('focus', focusChangeHandler);
|
||||
this.on('blur', focusChangeHandler);
|
||||
} else {
|
||||
this.off('focus', focusChangeHandler);
|
||||
this.off('blur', focusChangeHandler);
|
||||
|
||||
this[method]('focus', focusChangeHandler);
|
||||
this[method]('blur', focusChangeHandler);
|
||||
this._removeVisualState('focus');
|
||||
this._removeVisualState('blur');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -792,7 +792,7 @@ export class CssState {
|
||||
const eventName = ':' + pseudoClass;
|
||||
view.addEventListener(':' + pseudoClass, this._onDynamicStateChangeHandler);
|
||||
if (view[eventName]) {
|
||||
view[eventName](+1);
|
||||
view[eventName](true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -812,7 +812,7 @@ export class CssState {
|
||||
const eventName = ':' + pseudoClass;
|
||||
view.removeEventListener(eventName, this._onDynamicStateChangeHandler);
|
||||
if (view[eventName]) {
|
||||
view[eventName](-1);
|
||||
view[eventName](false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user