ScrollRect and OnPointerDown is broken.

I’m trying to create a list of players that can be re-ordered. I was using the basic ScrollRect to get the basic scrolling for the list if it goes off the screen. Then on the right side of the player there is a Button that if you click and hold, it will move the player up or down to be able to reorder the list of players. I’m using OnPointerDown to trigger that. Unfortunately, it seems that if the Button is a child of the ScrollRect, the OnPointerDown triggers properly but as soon as I move my mouse, the OnPointerUp triggers as well. I’ve only tested it with the mouse but if my Button is outside the ScrollRect, it works perfectly (albeit without the ScrollRect functionality I want). Inside the ScrollRect, it’s broken. I’ve even tried programmably removing and re-adding the ScrollRect Component and it still doesn’t work.

Any suggestions on a workaround or is this a bug?

Here’s some sample code I started on.

    void Update () {
        if (startClick) {

            float mousePosOffset = mouseStartPos.y - Input.mousePosition.y;
            float listItemOffset = listItemStartPos.y - mousePosOffset;

            Vector3 tempPos = new Vector3(parentTransform.position.x, listItemOffset, parentTransform.position.z);
            parentTransform.position = tempPos;

        }
    }

    public void OnPointerDown (PointerEventData eventData) {
        Debug.Log ("OnPointerDown");
        startClick = true;
        mouseStartPos = Input.mousePosition;
        listItemStartPos = parentTransform.position;
    }

    public void OnPointerUp (PointerEventData eventData) {
        Debug.Log ("OnPointerUp");
        startClick = false;
        parentTransform.position = listItemStartPos;
    }
1 Like

I have similar problem… It’s impossible to click button by finger on touch screen if button is child of scrollrect because button moves at this moment and loses focus.

it’s better than last version:p

Customizable drag threshold would solve that one, do vote on the bug if you haven’t already. :slight_smile:

Hi, I added this today (it won’t make b19 :frowning: ).

Basically when we start a drag you can register an interface for callback to fetch the DragThreshold (default for all dragging is 5 pixels, overridden and customizable in ScrollRect @ 15).

1 Like

it seems it is still working this way in 4.6.3…

Hello folks, I’d just like to drop my homebrew solution for this issue.

When the pointer begins, you climb up the transform parent tree and find the first active scroll rect.
(If none is found then the solution falls through without interference.)

You then cache the scroll rect and disable the component.

When your pointer leaves or finishes you re-enable the scroll rect and dump your reference.

Simple as pie, I hope you can repurpose the code or concept for your particular scenario.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class ScrollFix : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
    ////////////////////////////////

    private ScrollRect m_parentScrollRect;

    ////////////////////////////////

    public void OnPointerDown(PointerEventData eventData)
    {
        Transform query = this.transform;

        do
        {
            this.m_parentScrollRect = query.GetComponent<ScrollRect>();

            if ((null != this.m_parentScrollRect) && this.m_parentScrollRect && this.m_parentScrollRect.enabled)
            {
                break;
            }

            query = query.parent;
        }
        while ((null != query) && query && (null != query.parent) && query.parent);

        if ((null != this.m_parentScrollRect) && this.m_parentScrollRect)
        {
            this.m_parentScrollRect.enabled = false;
        }

        return;
    }

    ////////////////////////////////

    public void OnPointerUp(PointerEventData eventData)
    {
        this.OnPointerExit(eventData);

        return;
    }

    ////////////////////////////////

    public void OnPointerExit(PointerEventData eventData)
    {
        if ((null != this.m_parentScrollRect) && this.m_parentScrollRect)
        {
            this.m_parentScrollRect.enabled = true;

            // This is for Touch based input, ScrollRect needs to be notified for elastic movement.
            this.m_parentScrollRect.OnEndDrag(eventData);
     
            this.m_parentScrollRect = null;
        }
 
        return;
    }

    ////////////////////////////////

}

Thanks for this, and I’m from the future (unity 2019.4.20)…

I had problem with holding down cards to trigger an event, the cards are in a scroll rect obviously so it’s nearly impossible to not trigger a OnPointerUp with a finger, your solution works with me.

As a workaround you can implement an IDragHandler in your code with an empty OnDrag method to fix the issue. IDK why that works though.