There might be a better way of doing this, but I’ve solved a similar problem for myself with the eventSystem.
On your button: add a script implementing the IBeginDragHandler, IDragHandler, IEndDragHandler.
Within IBeginDragHandler, IDragHandler and IEndDragHandler pass on the event to your scrollRect with
ExecuteEvents.Execute(scrollRect.gameObject, pointerEventData, ExecuteEvents.beginDragHandler);
ExecuteEvents.Execute(scrollRect.gameObject, pointerEventData, ExecuteEvents.dragHandler);
ExecuteEvents.Execute(scrollRect.gameObject, pointerEventData, ExecuteEvents.endDragHandler);
respectively.
This makes your button send the same event (i.e. you clicking and dragging it) the button received to the scrollrect, making it scroll.
If you need to you can add some logic before initializing the first ExecuteEvents. In my case I made it so you have to drag it past a certain threshold before passing it on.
Below is a draft for how it could look. Untested as is
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class yourScript : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public ScrollRect yourScrollRect;
private bool passingEvent = false;
public void OnBeginDrag(PointerEventData pointerEventData)
{
// If you only need to pass the drag through use
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.beginDragHandler);
passingEvent = true;
}
public void OnDrag(PointerEventData pointerEventData)
{
if (passingEvent) // Don't send dragHandler before beginDragHandler has been called. It gives unwanted results...
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.dragHandler);
}
}
public void OnEndDrag(PointerEventData pointerEventData)
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.endDragHandler);
passingEvent = false;
}
}
And if you need logic to check whether the event should be passed on use something like this:
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class yourScript : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public ScrollRect yourScrollRect;
private Vector3 mousePosOnDragStart;
private bool passingEvent = false;
public void OnBeginDrag(PointerEventData pointerEventData)
{
mousePosOnDragStart = Input.mousePosition;
// Or something else you need to do at the start of the drag.
}
public void OnDrag(PointerEventData pointerEventData)
{
if ((Input.mousePosition - mousePosOnDragStart).sqrMagnitude > 1) // Checks if mouse has moved further than 1. Use your on logic here
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.beginDragHandler);
passingEvent = true;
}
if (passingEvent) // Don't send dragHandler before beginDragHandler has been called. It gives unwanted results...
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.dragHandler);
}
}
public void OnEndDrag(PointerEventData pointerEventData)
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.endDragHandler);
passingEvent = false;
}
}