NestedScrollManager.cs 소스입니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class NestedScrollManager : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public Scrollbar scrollbar;
public Transform contentTr;
public Slider tabSlider;
public RectTransform[] BtnRect, BtnImageRect;
const int SIZE = 4;
float[] pos = new float[SIZE];
float distance, curPos, targetPos;
bool isDrag;
int targetIndex;
void Start()
{
// 거리에 따라 0~1인 pos대입
distance = 1f / (SIZE - 1);
for (int i = 0; i < SIZE; i++) pos[i] = distance * i;
}
float SetPos()
{
// 절반거리를 기준으로 가까운 위치를 반환
for (int i = 0; i < SIZE; i++)
if (scrollbar.value < pos[i] + distance * 0.5f && scrollbar.value > pos[i] - distance * 0.5f)
{
targetIndex = i;
return pos[i];
}
return 0;
}
public void OnBeginDrag(PointerEventData eventData) => curPos = SetPos();
public void OnDrag(PointerEventData eventData) => isDrag = true;
public void OnEndDrag(PointerEventData eventData)
{
isDrag = false;
targetPos = SetPos();
// 절반거리를 넘지 않아도 마우스를 빠르게 이동하면
if (curPos == targetPos)
{
// ← 으로 가려면 목표가 하나 감소
if (eventData.delta.x > 18 && curPos - distance >= 0)
{
--targetIndex;
targetPos = curPos - distance;
}
// → 으로 가려면 목표가 하나 증가
else if (eventData.delta.x < -18 && curPos + distance <= 1.01f)
{
++targetIndex;
targetPos = curPos + distance;
}
}
VerticalScrollUp();
}
void VerticalScrollUp()
{
// 목표가 수직스크롤이고, 옆에서 옮겨왔다면 수직스크롤을 맨 위로 올림
for (int i = 0; i < SIZE; i++)
if (contentTr.GetChild(i).GetComponent<ScrollScript>() && curPos != pos[i] && targetPos == pos[i])
contentTr.GetChild(i).GetChild(1).GetComponent<Scrollbar>().value = 1;
}
void Update()
{
tabSlider.value = scrollbar.value;
if (!isDrag)
{
scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
// 목표 버튼은 크기가 커짐
for (int i = 0; i < SIZE; i++) BtnRect[i].sizeDelta = new Vector2(i == targetIndex ? 360 : 180, BtnRect[i].sizeDelta.y);
}
if (Time.time < 0.1f) return;
for (int i = 0; i < SIZE; i++)
{
// 버튼 아이콘이 부드럽게 버튼의 중앙으로 이동, 크기는 1, 텍스트 비활성화
Vector3 BtnTargetPos = BtnRect[i].anchoredPosition3D;
Vector3 BtnTargetScale = Vector3.one;
bool textActive = false;
// 선택한 버튼 아이콘은 약간 위로 올리고, 크기도 키우고, 텍스트도 활성화
if (i == targetIndex)
{
BtnTargetPos.y = -23f;
BtnTargetScale = new Vector3(1.2f, 1.2f, 1);
textActive = true;
}
BtnImageRect[i].anchoredPosition3D = Vector3.Lerp(BtnImageRect[i].anchoredPosition3D, BtnTargetPos, 0.25f);
BtnImageRect[i].localScale = Vector3.Lerp(BtnImageRect[i].localScale, BtnTargetScale, 0.25f);
BtnImageRect[i].transform.GetChild(0).gameObject.SetActive(textActive);
}
}
public void TabClick(int n)
{
curPos = SetPos();
targetIndex = n;
targetPos = pos[n];
VerticalScrollUp();
}
}
ScrollScript.cs 소스입니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class ScrollScript : ScrollRect
{
bool forParent;
NestedScrollManager NM;
ScrollRect parentScrollRect;
protected override void Start()
{
NM = GameObject.FindWithTag("NestedScrollManager").GetComponent<NestedScrollManager>();
parentScrollRect = GameObject.FindWithTag("NestedScrollManager").GetComponent<ScrollRect>();
}
public override void OnBeginDrag(PointerEventData eventData)
{
// 드래그 시작하는 순간 수평이동이 크면 부모가 드래그 시작한 것, 수직이동이 크면 자식이 드래그 시작한 것
forParent = Mathf.Abs(eventData.delta.x) > Mathf.Abs(eventData.delta.y);
if (forParent)
{
NM.OnBeginDrag(eventData);
parentScrollRect.OnBeginDrag(eventData);
}
else base.OnBeginDrag(eventData);
}
public override void OnDrag(PointerEventData eventData)
{
if (forParent)
{
NM.OnDrag(eventData);
parentScrollRect.OnDrag(eventData);
}
else base.OnDrag(eventData);
}
public override void OnEndDrag(PointerEventData eventData)
{
if (forParent)
{
NM.OnEndDrag(eventData);
parentScrollRect.OnEndDrag(eventData);
}
else base.OnEndDrag(eventData);
}
}
TabManager.cs 소스입니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TabManager : MonoBehaviour
{
public GameObject[] Tab;
public Image[] TabBtnImage;
public Sprite[] IdleSprite, SelectSprite;
void Start() => TabClick(0);
public void TabClick(int n)
{
for (int i = 0; i < Tab.Length; i++)
{
Tab[i].SetActive(i == n);
TabBtnImage[i].sprite = i == n ? SelectSprite[i] : IdleSprite[i];
}
}
}