Nativescript: longPress gesture fires twice

Created on 3 Feb 2017  ·  18Comments  ·  Source: NativeScript/NativeScript

Platform: iOS
TNS: 2.5.0

When using the longPress gesture, it will fire once (as it should) and then when you let go of your finger, it fires again. This isn't intentional, is it?

There's no state or action property of the event, so there's no such thing as an "up" event to handle.


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

bug ios medium

Most helpful comment

@farfromrefug @fgutteridge I've opened this PR which is proving the state as an argument for longPress event on iOS (via GestureEventDataWithState interface).

Example:

export function testLongPress(args: GestureEventDataWithState) {
    console.log(`LongPress fired!`);
    console.log(`>>> args.state ${args.state}`);
}

Note: As Android doesn't support states I've made it so it would always return state 1 when the long press is triggered on Android

All 18 comments

Hi @NordlingArt,
Thank you for reporting this issue.
I was able to reproduce this behavior on my side and confirm that this is a real issue for iOS. I am also attaching sample code, where the problem could be reproduced.

main-page.xml

<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">

    <Page.actionBar>
        <ActionBar title="My App" icon="" class="action-bar">
        </ActionBar>
    </Page.actionBar>

    <StackLayout class="p-20">
        <Label loaded="labelLoaded" text="Tap the button" class="h1 text-center" />
        <Button text="TAP" tap="{{ onTap }}" class="btn btn-primary btn-active"/>
        <Label text="{{ message }}" class="h2 text-center" textWrap="true"/>
    </StackLayout>
</Page>

main-page.ts

import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
import { GestureTypes, GestureEventData, TouchGestureEventData } from "ui/gestures";
import labelModule = require("ui/label");
import {isIOS} from "platform"

// Event handler for Page "navigatingTo" event attached in main-page.xml
var viewmodel = new HelloWorldModel()
var statedown=true
export function navigatingTo(args: EventData) {

    let page = <Page>args.object;

    page.bindingContext = viewmodel;
}

export function labelLoaded(args){
    var label = args.object;
    label.on("longPress", function (args: GestureEventData) {
        console.log("longPress");
                console.log(args.eventName);
                console.log(args.type);


    });


}

As a workaround, you could create one custom flag, which will allow you to determine the second fire of the event on iOS.

main-page.ts

import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
import { GestureTypes, GestureEventData, TouchGestureEventData } from "ui/gestures";
import labelModule = require("ui/label");
import {isIOS} from "platform"

// Event handler for Page "navigatingTo" event attached in main-page.xml
var viewmodel = new HelloWorldModel()
var statedown=true
export function navigatingTo(args: EventData) {

    let page = <Page>args.object;

    page.bindingContext = viewmodel;
}

export function labelLoaded(args){
    var label = args.object;
    label.on("longPress", function (args: GestureEventData) {
        console.log(statedown)
        if(isIOS){
            if(statedown){
                console.log("longPress");
                console.log(args.eventName);
                console.log(args.type);
                statedown=false;
            }
            else{
                statedown=true;
            }
        }


    });


}

longPress gesture does not have state or action property. For this case, you could use touch gesture, where you could handle the state while using its action property.

label.on(GestureTypes.touch, function (args: TouchGestureEventData) {
        console.log("Touch: x: " + args.getX() + " y: " + args.getY());
        console.log(args.action)
    });

@tsonevn - Thanks for confirming issue. I have applied a custom flag as a temporary work around.

Please note the event actually fires multiple times; try holding the mouse down and then moving while doing a long press....

Confirmed, just ran into this.

Having the same, any updates?

Hi @terreb,
This is still a valid issue for iOS. Regarding that, this problem is still under review and I could not commit to the exact time, where it would be fixed. You could keep track on the issue for further info and in the meantime, you could use the above-given workaround.

@tsonevn, ok thank you.

TNS 3.2.1, iOS

Having the same, any updates?

Base on the iOS documentation, we could check the state of the gesture and to allow executing our functionality only on long press ended.

In the meantime, we could workaround this case as follow:

<Label textWrap="true" text="Play with NativeScript!"v backgroundColor="green" height="200"
         class="h2 description-label" loaded="labelLoaded"/>
import { GestureTypes,  GestureEventData } from "tns-core-modules/ui/gestures";
abelLoaded(args) {
        console.log("add gesture- label: " + NgZone.isInAngularZone());
        let label: Label = <Label>args.object;
        label.on(GestureTypes.longPress, (args: GestureEventData) => {
            if (args.ios.state !== 3)
                return;

            console.log("long press");

        });
    }

reproducible with modules 4.1

This is not a bug but a feature of iOS I guess. We should just check the state on iOS. Should this issue still be opened?

Can you explain why triggering a method can be a feature?

They implemented states into event. You can track when it starts, continues and ends. That means it is clearly something intended. Not a bug. Maybe you can use this to drag something around. It would be handy if you could track that.

// Assume platform is iOS
exports.onLongPress = args => {
    switch(args.ios.state) {
        case UIGestureRecognizerStateBegan: // 1
            // The long press event has begun.
            break;
        case UIGestureRecognizerStateChanged: // 2
            // The long press event has changed.
            break;
        case UIGestureRecognizerStateEnded: // 3
            // The long press event has ended.
            break;
    }
}

A good thing would be to return the state directly in the gesture event and in the ios object. It is the case for some gesture events but not all.

Hey @NickIliev...

Just ran in to this issue myself ({N}6.3), and while the solution provided above (manually checking the UIGestureRecognizer's state) works, I wondered if this will be fixed in upcoming releases of NS...

These are the sorts of things that (I feel) are holding NativeScript back––weird, inconsistent behaviour across versions, platforms, etc. (though they are also the hardest to perfect, of course)

Thanks for your help, @tsonevn and @NickIliev!

@farfromrefug @fgutteridge I've opened this PR which is proving the state as an argument for longPress event on iOS (via GestureEventDataWithState interface).

Example:

export function testLongPress(args: GestureEventDataWithState) {
    console.log(`LongPress fired!`);
    console.log(`>>> args.state ${args.state}`);
}

Note: As Android doesn't support states I've made it so it would always return state 1 when the long press is triggered on Android

Looks great, @NickIliev! Thanks so much for your help. Yes, having the longPress event exposed statefully (for iOS at least) makes the most sense.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

atanasovg picture atanasovg  ·  50Comments

valentinstoychev picture valentinstoychev  ·  70Comments

surdu picture surdu  ·  63Comments

valentinstoychev picture valentinstoychev  ·  136Comments

rclai picture rclai  ·  52Comments