fix(button): ios let the gesture observer handles tap events

in some weird cases like i some UICollectionView layouts the UIControlEvents.TouchUpInside is failing
This commit is contained in:
Martin Guillon
2021-03-18 14:00:36 +01:00
parent 56a3f9bd3c
commit a2d06a9efe
19 changed files with 62 additions and 59 deletions

View File

@ -51,7 +51,7 @@ export function createPage() {
//page.content = GridLayout; //page.content = GridLayout;
page.content = StackLayout; page.content = StackLayout;
var x = 1; var x = 1;
btn1.on(button.Button.tapEvent, function () { btn1.on(button.tapEvent, function () {
x++; x++;
var gravity; var gravity;
//btn1.android.setLayoutParams(new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.FILL_PARENT)); //btn1.android.setLayoutParams(new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.FILL_PARENT));

View File

@ -13,7 +13,7 @@ export function createPage() {
var vAligns: VerticalAlignment[] = ['stretch', 'top', 'middle', 'bottom']; var vAligns: VerticalAlignment[] = ['stretch', 'top', 'middle', 'bottom'];
//var hAligns = ["stretch", "left", "center", "right"]; //var hAligns = ["stretch", "left", "center", "right"];
var count = 0; var count = 0;
btn.on(buttons.Button.tapEvent, function () { btn.on(buttons.tapEvent, function () {
//page.css = "button { vertical-align:" + vAligns[(count++) % 4] + " }"; //page.css = "button { vertical-align:" + vAligns[(count++) % 4] + " }";
btn.verticalAlignment = vAligns[count++ % 4]; btn.verticalAlignment = vAligns[count++ % 4];
}); });

View File

@ -41,7 +41,7 @@ export function createPage() {
var counter = 0; var counter = 0;
var btn = new btns.Button(); var btn = new btns.Button();
btn.text = 'tap'; btn.text = 'tap';
btn.on(btns.Button.tapEvent, function () { btn.on(btns.tapEvent, function () {
btn.text = 'hi: ' + counter++; btn.text = 'hi: ' + counter++;
}); });
btn.isEnabled = false; btn.isEnabled = false;
@ -52,7 +52,7 @@ export function createPage() {
var info = new btns.Button(); var info = new btns.Button();
info.text = 'info'; info.text = 'info';
info.className = 'info'; info.className = 'info';
info.on(btns.Button.tapEvent, function () { info.on(btns.tapEvent, function () {
info.text = 'hi: ' + counter++; info.text = 'hi: ' + counter++;
btn.isEnabled = true; btn.isEnabled = true;
}); });

View File

@ -41,7 +41,7 @@ export function createPage() {
var count = 0; var count = 0;
var control = new btns.Button(); var control = new btns.Button();
control.text = 'test control'; control.text = 'test control';
control.on(btns.Button.tapEvent, (data) => { control.on(btns.tapEvent, (data) => {
control.text = 'count: ' + count++; control.text = 'count: ' + count++;
}); });
grid.addChild(control); grid.addChild(control);

View File

@ -6,7 +6,7 @@ import { Frame } from '@nativescript/core/ui/frame';
import { Page } from '@nativescript/core/ui/page'; import { Page } from '@nativescript/core/ui/page';
import { ListView, ItemEventData } from '@nativescript/core/ui/list-view'; import { ListView, ItemEventData } from '@nativescript/core/ui/list-view';
import { BottomNavigation, TabContentItem, TabStrip, TabStripItem } from '@nativescript/core'; import { BottomNavigation, TabContentItem, TabStrip, TabStripItem } from '@nativescript/core';
import { Button } from '@nativescript/core/ui/button'; import { Button , tapEvent} from '@nativescript/core/ui/button';
var ASYNC = 2; var ASYNC = 2;
@ -55,7 +55,7 @@ function _createListView(): ListView {
var button = <Button>args.view; var button = <Button>args.view;
if (!button) { if (!button) {
button = new Button(); button = new Button();
button.on(Button.tapEvent, _clickHandlerFactory(args.index)); button.on(tapEvent, _clickHandlerFactory(args.index));
args.view = button; args.view = button;
} }

View File

@ -3,6 +3,7 @@ import * as helper from '../../ui-helper';
import { View, EventData, Button, Observable, Color, Page, FormattedString } from '@nativescript/core'; import { View, EventData, Button, Observable, Color, Page, FormattedString } from '@nativescript/core';
import * as buttonTestsNative from './button-tests-native'; import * as buttonTestsNative from './button-tests-native';
import * as spanModule from '@nativescript/core/text/span'; import * as spanModule from '@nativescript/core/text/span';
import { tapEvent} from '@nativescript/core/ui/button';
// >> button-require-others // >> button-require-others
import { BindingOptions } from '@nativescript/core/ui/core/bindable'; import { BindingOptions } from '@nativescript/core/ui/core/bindable';
@ -100,7 +101,7 @@ var _testOnClick = function (views: Array<View>) {
var actualValue = false; var actualValue = false;
// >> button-tap // >> button-tap
button.on(Button.tapEvent, function (args: EventData) { button.on(tapEvent, function (args: EventData) {
// Do something // Do something
// >> (hide) // >> (hide)
actualValue = true; actualValue = true;

View File

@ -13,7 +13,7 @@ export class MyControl extends stackLayoutModule.StackLayout {
lbl.id = 'my-test-label'; lbl.id = 'my-test-label';
var btn = new button.Button(); var btn = new button.Button();
btn.text = 'Tap me!'; btn.text = 'Tap me!';
btn.on(button.Button.tapEvent, (args: EventData) => { btn.on(button.tapEvent, (args: EventData) => {
lbl.text = 'Tap ' + counter++; lbl.text = 'Tap ' + counter++;
}); });

View File

@ -7,7 +7,7 @@ import { Frame } from '@nativescript/core/ui/frame';
import { Page } from '@nativescript/core/ui/page'; import { Page } from '@nativescript/core/ui/page';
import { ListView, ItemEventData } from '@nativescript/core/ui/list-view'; import { ListView, ItemEventData } from '@nativescript/core/ui/list-view';
import { TabView, TabViewItem } from '@nativescript/core/ui/tab-view'; import { TabView, TabViewItem } from '@nativescript/core/ui/tab-view';
import { Button } from '@nativescript/core/ui/button'; import { Button, tapEvent} from '@nativescript/core/ui/button';
var ASYNC = 2; var ASYNC = 2;
@ -44,7 +44,7 @@ function _createListView(): ListView {
var button = <Button>args.view; var button = <Button>args.view;
if (!button) { if (!button) {
button = new Button(); button = new Button();
button.on(Button.tapEvent, _clickHandlerFactory(args.index)); button.on(tapEvent, _clickHandlerFactory(args.index));
args.view = button; args.view = button;
} }

View File

@ -1,4 +1,5 @@
import { StackLayout, Label, Button, EventData } from '@nativescript/core'; import { StackLayout, Label, Button, EventData } from '@nativescript/core';
import { tapEvent } from '@nativescript/core/ui/button';
export class MyControl extends StackLayout { export class MyControl extends StackLayout {
constructor() { constructor() {
@ -9,7 +10,7 @@ export class MyControl extends StackLayout {
var lbl = new Label(); var lbl = new Label();
var btn = new Button(); var btn = new Button();
btn.text = 'Tap me!'; btn.text = 'Tap me!';
btn.on(Button.tapEvent, (args: EventData) => { btn.on(tapEvent, (args: EventData) => {
lbl.text = 'Tap ' + counter++; lbl.text = 'Tap ' + counter++;
}); });

View File

@ -28,7 +28,7 @@ export function createPage() {
//buttonTwoWay.automationText = "buttonTwoWay"; //buttonTwoWay.automationText = "buttonTwoWay";
buttonSetText.text = 'SetText'; buttonSetText.text = 'SetText';
buttonSetText.on(buttonModule.Button.tapEvent, function () { buttonSetText.on(buttonModule.tapEvent, function () {
targetOneWay.text = 'Test'; targetOneWay.text = 'Test';
targetTwoWay.text = 'Test'; targetTwoWay.text = 'Test';
}); });
@ -47,7 +47,7 @@ export function createPage() {
buttonOneWay.on(buttonModule.Button.loadedEvent, function () { buttonOneWay.on(buttonModule.Button.loadedEvent, function () {
buttonOneWay.text = sourceOneWay.get('textSource'); buttonOneWay.text = sourceOneWay.get('textSource');
}); });
buttonOneWay.on(buttonModule.Button.tapEvent, function () { buttonOneWay.on(buttonModule.tapEvent, function () {
buttonOneWay.text = sourceOneWay.get('textSource'); buttonOneWay.text = sourceOneWay.get('textSource');
}); });
@ -67,7 +67,7 @@ export function createPage() {
buttonTwoWay.on(buttonModule.Button.loadedEvent, function () { buttonTwoWay.on(buttonModule.Button.loadedEvent, function () {
buttonTwoWay.text = sourceTwoWay.get('textSource'); buttonTwoWay.text = sourceTwoWay.get('textSource');
}); });
buttonTwoWay.on(buttonModule.Button.tapEvent, function () { buttonTwoWay.on(buttonModule.tapEvent, function () {
buttonTwoWay.text = sourceTwoWay.get('textSource'); buttonTwoWay.text = sourceTwoWay.get('textSource');
}); });

View File

@ -16,7 +16,7 @@ export function stack0Loaded(args: observable.EventData) {
target.bind(bindingOptions, source); target.bind(bindingOptions, source);
source.set('textSource', 'Text'); source.set('textSource', 'Text');
button.on(buttonModule.Button.tapEvent, function () { button.on(buttonModule.tapEvent, function () {
button.text = source.get('textSource'); button.text = source.get('textSource');
}); });
} }

View File

@ -7,7 +7,7 @@ export function createPage() {
var btn = new button.Button(); var btn = new button.Button();
btn.text = 'Alert'; btn.text = 'Alert';
btn.on(button.Button.tapEvent, function () { btn.on(button.tapEvent, function () {
alert('Alert is global'); alert('Alert is global');
}); });

View File

@ -1,5 +1,5 @@
import { Observable } from '@nativescript/core/data/observable'; import { Observable } from '@nativescript/core/data/observable';
import { Button } from '@nativescript/core/ui/button'; import { Button, tapEvent } from '@nativescript/core/ui/button';
import { Color } from '@nativescript/core/color'; import { Color } from '@nativescript/core/color';
import { WrapLayout } from '@nativescript/core/ui/layouts/wrap-layout'; import { WrapLayout } from '@nativescript/core/ui/layouts/wrap-layout';
import { alert } from '@nativescript/core/ui/dialogs'; import { alert } from '@nativescript/core/ui/dialogs';
@ -65,7 +65,7 @@ export class TestPageMainViewModel extends Observable {
btn.style.backgroundColor = new Color(this._colors[count++ % 3]); btn.style.backgroundColor = new Color(this._colors[count++ % 3]);
btn.style.borderRadius = 5; btn.style.borderRadius = 5;
btn.on( btn.on(
Button.tapEvent, tapEvent,
function (eventData) { function (eventData) {
let text = btn.text; let text = btn.text;
this.loadExample(text); this.loadExample(text);

View File

@ -259,6 +259,8 @@ export class ActionItems implements ActionItemsDefinition {
} }
export class ActionItemBase extends ViewBase implements ActionItemDefinition { export class ActionItemBase extends ViewBase implements ActionItemDefinition {
// defining this here means tap event wont be handled
// by the gesture observers
public static tapEvent = 'tap'; public static tapEvent = 'tap';
private _actionBar: ActionBarDefinition; private _actionBar: ActionBarDefinition;

View File

@ -3,9 +3,11 @@ import { TextBase } from '../text-base';
import { CSSType } from '../core/view'; import { CSSType } from '../core/view';
import { booleanConverter } from '../core/view-base'; import { booleanConverter } from '../core/view-base';
export const tapEvent = 'tap';
@CSSType('Button') @CSSType('Button')
export abstract class ButtonBase extends TextBase implements ButtonDefinition { export abstract class ButtonBase extends TextBase implements ButtonDefinition {
public static tapEvent = 'tap';
get textWrap(): boolean { get textWrap(): boolean {
return this.style.whiteSpace === 'normal'; return this.style.whiteSpace === 'normal';

View File

@ -1,4 +1,4 @@
import { ButtonBase } from './button-common'; import { ButtonBase, tapEvent } from './button-common';
import { PseudoClassHandler } from '../core/view'; import { PseudoClassHandler } from '../core/view';
import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length, zIndexProperty, minWidthProperty, minHeightProperty } from '../styling/style-properties'; import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length, zIndexProperty, minWidthProperty, minHeightProperty } from '../styling/style-properties';
import { textAlignmentProperty } from '../text-base'; import { textAlignmentProperty } from '../text-base';
@ -38,7 +38,7 @@ function initializeClickListener(): void {
public onClick(v: android.view.View): void { public onClick(v: android.view.View): void {
const owner = this.owner; const owner = this.owner;
if (owner) { if (owner) {
owner._emit(ButtonBase.tapEvent); owner._emit(tapEvent);
} }
} }
} }
@ -47,6 +47,9 @@ function initializeClickListener(): void {
} }
export class Button extends ButtonBase { export class Button extends ButtonBase {
// defining this here means tap event wont be handled
// by the gesture observers
public static tapEvent = tapEvent;
nativeViewProtected: android.widget.Button; nativeViewProtected: android.widget.Button;
constructor() { constructor() {

View File

@ -1,14 +1,16 @@
import { TextBase } from '../text-base'; import { TextBase } from '../text-base';
import { EventData } from '../../data/observable'; import { EventData } from '../../data/observable';
/**
* String value used when hooking to tap event.
*/
export const tapEvent: string;
/** /**
* Represents a standard Button widget. * Represents a standard Button widget.
*/ */
export class Button extends TextBase { export class Button extends TextBase {
/**
* String value used when hooking to tap event.
*/
public static tapEvent: string;
/** /**
* Gets the native [android widget](http://developer.android.com/reference/android/widget/Button.html) that represents the user interface for this component. Valid only when running on Android OS. * Gets the native [android widget](http://developer.android.com/reference/android/widget/Button.html) that represents the user interface for this component. Valid only when running on Android OS.

View File

@ -12,6 +12,8 @@ import { layout } from '../../utils';
export * from './button-common'; export * from './button-common';
export class Button extends ButtonBase { export class Button extends ButtonBase {
// we dont defile tapEvent to let the gesture obserers handle it
// in some weird case with UICollectionView UIControlEvents.TouchUpInside is not working
public nativeViewProtected: UIButton; public nativeViewProtected: UIButton;
private _tapHandler: NSObject; private _tapHandler: NSObject;
@ -21,18 +23,6 @@ export class Button extends ButtonBase {
return UIButton.buttonWithType(UIButtonType.System); return UIButton.buttonWithType(UIButtonType.System);
} }
public initNativeView(): void {
super.initNativeView();
const nativeView = this.nativeViewProtected;
this._tapHandler = TapHandlerImpl.initWithOwner(new WeakRef(this));
nativeView.addTargetActionForControlEvents(this._tapHandler, 'tap', UIControlEvents.TouchUpInside);
}
public disposeNativeView(): void {
this._tapHandler = null;
super.disposeNativeView();
}
// @ts-ignore // @ts-ignore
get ios() { get ios() {
return this.nativeViewProtected; return this.nativeViewProtected;
@ -266,27 +256,27 @@ export class Button extends ButtonBase {
} }
} }
@NativeClass // @NativeClass
class TapHandlerImpl extends NSObject { // class TapHandlerImpl extends NSObject {
private _owner: WeakRef<Button>; // private _owner: WeakRef<Button>;
public static initWithOwner(owner: WeakRef<Button>): TapHandlerImpl { // public static initWithOwner(owner: WeakRef<Button>): TapHandlerImpl {
const handler = <TapHandlerImpl>TapHandlerImpl.new(); // const handler = <TapHandlerImpl>TapHandlerImpl.new();
handler._owner = owner; // handler._owner = owner;
return handler; // return handler;
} // }
public tap(args) { // public tap(args) {
// _owner is a {N} view which could get destroyed when a tap initiates (protect!) // // _owner is a {N} view which could get destroyed when a tap initiates (protect!)
if (this._owner) { // if (this._owner) {
const owner = this._owner.get(); // const owner = this._owner.get();
if (owner) { // if (owner) {
owner._emit(ButtonBase.tapEvent); // owner._emit(ButtonBase.tapEvent);
} // }
} // }
} // }
public static ObjCExposedMethods = { // public static ObjCExposedMethods = {
tap: { returns: interop.types.void, params: [interop.types.id] }, // tap: { returns: interop.types.void, params: [interop.types.id] },
}; // };
} // }

View File

@ -16,6 +16,8 @@ import { textTransformProperty, TextTransform } from '../../text-base';
@CSSType('TabStripItem') @CSSType('TabStripItem')
export class TabStripItem extends View implements TabStripItemDefinition, AddChildFromBuilder { export class TabStripItem extends View implements TabStripItemDefinition, AddChildFromBuilder {
// defining this here means tap event wont be handled
// by the gesture observers
public static tapEvent = 'tap'; public static tapEvent = 'tap';
public static selectEvent = 'select'; public static selectEvent = 'select';
public static unselectEvent = 'unselect'; public static unselectEvent = 'unselect';