Added Scrollbar helper to ALL current scroll snaps

Resolves #120
release
Simon Jackson 2017-07-02 10:09:52 +01:00
parent 23deee414a
commit 49ecdc1b0e
8 changed files with 960 additions and 573 deletions

View File

@ -379,7 +379,7 @@ namespace UnityEditor.UI
ScrollSnap scrollSnap = scrollSnapRoot.AddComponent<ScrollSnap>(); ScrollSnap scrollSnap = scrollSnapRoot.AddComponent<ScrollSnap>();
scrollSnap.direction = direction; scrollSnap.direction = direction;
scrollSnap.itemsVisibleAtOnce = itemVisible; scrollSnap.ItemsVisibleAtOnce = itemVisible;
//Setup Content container //Setup Content container
RectTransform rectTransformContent = itemList.GetComponent<RectTransform>(); RectTransform rectTransformContent = itemList.GetComponent<RectTransform>();

View File

@ -290,6 +290,7 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: m_Children:
- {fileID: 1826600021} - {fileID: 1826600021}
- {fileID: 472879517}
m_Father: {fileID: 448991457} m_Father: {fileID: 448991457}
m_RootOrder: 0 m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -392,7 +393,7 @@ MonoBehaviour:
m_DecelerationRate: 0.135 m_DecelerationRate: 0.135
m_ScrollSensitivity: 1 m_ScrollSensitivity: 1
m_Viewport: {fileID: 0} m_Viewport: {fileID: 0}
m_HorizontalScrollbar: {fileID: 0} m_HorizontalScrollbar: {fileID: 472879516}
m_VerticalScrollbar: {fileID: 0} m_VerticalScrollbar: {fileID: 0}
m_HorizontalScrollbarVisibility: 0 m_HorizontalScrollbarVisibility: 0
m_VerticalScrollbarVisibility: 0 m_VerticalScrollbarVisibility: 0
@ -899,6 +900,122 @@ Canvas:
m_SortingLayerID: 0 m_SortingLayerID: 0
m_SortingOrder: 0 m_SortingOrder: 0
m_TargetDisplay: 0 m_TargetDisplay: 0
--- !u!1 &472879515
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 5
m_Component:
- component: {fileID: 472879517}
- component: {fileID: 472879519}
- component: {fileID: 472879518}
- component: {fileID: 472879516}
m_Layer: 5
m_Name: Scrollbar
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &472879516
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 472879515}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -2061169968, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 557949556}
m_HandleRect: {fileID: 557949554}
m_Direction: 0
m_Value: 0
m_Size: 1
m_NumberOfSteps: 0
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.Scrollbar+ScrollEvent, UnityEngine.UI, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null
--- !u!224 &472879517
RectTransform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 472879515}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 1513549040}
m_Father: {fileID: 67156822}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -2.6999998, y: -73.5}
m_SizeDelta: {x: 384.7, y: 20}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &472879518
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 472879515}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!222 &472879519
CanvasRenderer:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 472879515}
--- !u!1 &475765631 --- !u!1 &475765631
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -1116,6 +1233,74 @@ CanvasRenderer:
m_PrefabParentObject: {fileID: 0} m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0} m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 546976065} m_GameObject: {fileID: 546976065}
--- !u!1 &557949553
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 5
m_Component:
- component: {fileID: 557949554}
- component: {fileID: 557949555}
- component: {fileID: 557949556}
m_Layer: 5
m_Name: Handle
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &557949554
RectTransform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 557949553}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1513549040}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 20, y: 20}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &557949555
CanvasRenderer:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 557949553}
--- !u!114 &557949556
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 557949553}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!1 &571794653 --- !u!1 &571794653
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -1707,7 +1892,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1} m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -122.5, y: -176.5} m_AnchoredPosition: {x: -113.9, y: -183.2}
m_SizeDelta: {x: 150, y: 30} m_SizeDelta: {x: 150, y: 30}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &803284191 --- !u!114 &803284191
@ -2525,7 +2710,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1} m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -335.5, y: -176.5} m_AnchoredPosition: {x: -326.9, y: -183.2}
m_SizeDelta: {x: 150, y: 30} m_SizeDelta: {x: 150, y: 30}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1112321046 --- !u!114 &1112321046
@ -3612,6 +3797,40 @@ CanvasRenderer:
m_PrefabParentObject: {fileID: 0} m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0} m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1496412125} m_GameObject: {fileID: 1496412125}
--- !u!1 &1513549039
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 5
m_Component:
- component: {fileID: 1513549040}
m_Layer: 5
m_Name: Sliding Area
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1513549040
RectTransform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1513549039}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 557949554}
m_Father: {fileID: 472879517}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: -20, y: -20}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1520368422 --- !u!1 &1520368422
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@ -0,0 +1,14 @@
/// Credit SimonDarksideJ
/// Required for scrollbar support to work across ALL scroll snaps
namespace UnityEngine.UI.Extensions
{
internal interface IScrollSnap
{
void ChangePage(int page);
void SetLerp(bool value);
int CurrentPage();
void StartScreenChange();
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 08ba470d8c8dc384e9d5828136742452
timeCreated: 1498921617
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -17,7 +17,7 @@ namespace UnityEngine.UI.Extensions
[ExecuteInEditMode] [ExecuteInEditMode]
[RequireComponent(typeof(ScrollRect))] [RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("UI/Extensions/Scroll Snap")] [AddComponentMenu("UI/Extensions/Scroll Snap")]
public class ScrollSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler public class ScrollSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollSnap
{ {
// needed because of reversed behaviour of axis Y compared to X // needed because of reversed behaviour of axis Y compared to X
// (positions of children lower in children list in horizontal directions grows when in vertical it gets smaller) // (positions of children lower in children list in horizontal directions grows when in vertical it gets smaller)
@ -27,118 +27,123 @@ namespace UnityEngine.UI.Extensions
Vertical Vertical
} }
private ScrollRect _scroll_rect;
private RectTransform _scrollRectTransform;
private Transform _listContainerTransform;
private RectTransform _rectTransform;
private int _pages;
private int _startingPage = 0;
// anchor points to lerp to to see child on certain indexes
private Vector3[] _pageAnchorPositions;
private Vector3 _lerpTarget;
private bool _lerp;
// item list related
private float _listContainerMinPosition;
private float _listContainerMaxPosition;
private float _listContainerSize;
private RectTransform _listContainerRectTransform;
private Vector2 _listContainerCachedSize;
private float _itemSize;
private int _itemsCount = 0;
// drag related
private bool _startDrag = true;
private Vector3 _positionOnDragStart = new Vector3();
private int _pageOnDragStart;
private bool _fastSwipeTimer = false;
private int _fastSwipeCounter = 0;
private int _fastSwipeTarget = 10;
[Tooltip("Button to go to the next page. (optional)")]
public Button NextButton;
[Tooltip("Button to go to the previous page. (optional)")]
public Button PrevButton;
[Tooltip("Number of items visible in one page of scroll frame.")]
[RangeAttribute(1, 100)]
public int ItemsVisibleAtOnce = 1;
[Tooltip("Sets minimum width of list items to 1/itemsVisibleAtOnce.")]
public bool AutoLayoutItems = true;
[Tooltip("If you wish to update scrollbar numberOfSteps to number of active children on list.")]
public bool LinkScrolbarSteps = false;
[Tooltip("If you wish to update scrollrect sensitivity to size of list element.")]
public bool LinkScrolrectScrollSensitivity = false;
public Boolean UseFastSwipe = true;
public int FastSwipeThreshold = 100;
public delegate void PageSnapChange(int page); public delegate void PageSnapChange(int page);
public event PageSnapChange onPageChange; public event PageSnapChange onPageChange;
public ScrollDirection direction = ScrollDirection.Horizontal; public ScrollDirection direction = ScrollDirection.Horizontal;
protected ScrollRect scrollRect;
protected RectTransform scrollRectTransform;
protected Transform listContainerTransform;
protected RectTransform rectTransform;
int pages;
protected int startingPage = 0;
// anchor points to lerp to to see child on certain indexes
protected Vector3[] pageAnchorPositions;
protected Vector3 lerpTarget;
protected bool lerp;
// item list related
protected float listContainerMinPosition;
protected float listContainerMaxPosition;
protected float listContainerSize;
protected RectTransform listContainerRectTransform;
protected Vector2 listContainerCachedSize;
protected float itemSize;
protected int itemsCount = 0;
[Tooltip("Button to go to the next page. (optional)")]
public Button nextButton;
[Tooltip("Button to go to the previous page. (optional)")]
public Button prevButton;
[Tooltip("Number of items visible in one page of scroll frame.")]
[RangeAttribute(1, 100)]
public int itemsVisibleAtOnce = 1;
[Tooltip("Sets minimum width of list items to 1/itemsVisibleAtOnce.")]
public bool autoLayoutItems = true;
[Tooltip("If you wish to update scrollbar numberOfSteps to number of active children on list.")]
public bool linkScrolbarSteps = false;
[Tooltip("If you wish to update scrollrect sensitivity to size of list element.")]
public bool linkScrolrectScrollSensitivity = false;
public Boolean useFastSwipe = true;
public int fastSwipeThreshold = 100;
// drag related
protected bool startDrag = true;
protected Vector3 positionOnDragStart = new Vector3();
protected int pageOnDragStart;
protected bool fastSwipeTimer = false;
protected int fastSwipeCounter = 0;
protected int fastSwipeTarget = 10;
// Use this for initialization // Use this for initialization
void Awake() void Start()
{ {
lerp = false; _lerp = false;
scrollRect = gameObject.GetComponent<ScrollRect>(); _scroll_rect = gameObject.GetComponent<ScrollRect>();
scrollRectTransform = gameObject.GetComponent<RectTransform>(); _scrollRectTransform = gameObject.GetComponent<RectTransform>();
listContainerTransform = scrollRect.content; _listContainerTransform = _scroll_rect.content;
listContainerRectTransform = listContainerTransform.GetComponent<RectTransform>(); _listContainerRectTransform = _listContainerTransform.GetComponent<RectTransform>();
rectTransform = listContainerTransform.gameObject.GetComponent<RectTransform>(); _rectTransform = _listContainerTransform.gameObject.GetComponent<RectTransform>();
UpdateListItemsSize(); UpdateListItemsSize();
UpdateListItemPositions(); UpdateListItemPositions();
PageChanged(CurrentPage()); PageChanged(CurrentPage());
if (nextButton) if (NextButton)
{ {
nextButton.GetComponent<Button>().onClick.AddListener(() => NextButton.GetComponent<Button>().onClick.AddListener(() =>
{ {
NextScreen(); NextScreen();
}); });
} }
if (prevButton) if (PrevButton)
{ {
prevButton.GetComponent<Button>().onClick.AddListener(() => PrevButton.GetComponent<Button>().onClick.AddListener(() =>
{ {
PreviousScreen(); PreviousScreen();
}); });
} }
} if (_scroll_rect.horizontalScrollbar != null && _scroll_rect.horizontal)
{
void Start() var hscroll = _scroll_rect.horizontalScrollbar.gameObject.AddComponent<ScrollSnapScrollbarHelper>();
{ hscroll.ss = this;
Awake(); }
if (_scroll_rect.verticalScrollbar != null && _scroll_rect.vertical)
{
var vscroll = _scroll_rect.verticalScrollbar.gameObject.AddComponent<ScrollSnapScrollbarHelper>();
vscroll.ss = this;
}
} }
public void UpdateListItemsSize() public void UpdateListItemsSize()
@ -147,27 +152,27 @@ namespace UnityEngine.UI.Extensions
float currentSize = 0; float currentSize = 0;
if (direction == ScrollSnap.ScrollDirection.Horizontal) if (direction == ScrollSnap.ScrollDirection.Horizontal)
{ {
size = scrollRectTransform.rect.width / itemsVisibleAtOnce; size = _scrollRectTransform.rect.width / ItemsVisibleAtOnce;
currentSize = listContainerRectTransform.rect.width / itemsCount; currentSize = _listContainerRectTransform.rect.width / _itemsCount;
} }
else else
{ {
size = scrollRectTransform.rect.height / itemsVisibleAtOnce; size = _scrollRectTransform.rect.height / ItemsVisibleAtOnce;
currentSize = listContainerRectTransform.rect.height / itemsCount; currentSize = _listContainerRectTransform.rect.height / _itemsCount;
} }
itemSize = size; _itemSize = size;
if (linkScrolrectScrollSensitivity) if (LinkScrolrectScrollSensitivity)
{ {
scrollRect.scrollSensitivity = itemSize; _scroll_rect.scrollSensitivity = _itemSize;
} }
if (autoLayoutItems && currentSize != size && itemsCount > 0) if (AutoLayoutItems && currentSize != size && _itemsCount > 0)
{ {
if (direction == ScrollSnap.ScrollDirection.Horizontal) if (direction == ScrollSnap.ScrollDirection.Horizontal)
{ {
foreach (var tr in listContainerTransform) foreach (var tr in _listContainerTransform)
{ {
GameObject child = ((Transform)tr).gameObject; GameObject child = ((Transform)tr).gameObject;
if (child.activeInHierarchy) if (child.activeInHierarchy)
@ -179,13 +184,13 @@ namespace UnityEngine.UI.Extensions
childLayout = child.AddComponent<LayoutElement>(); childLayout = child.AddComponent<LayoutElement>();
} }
childLayout.minWidth = itemSize; childLayout.minWidth = _itemSize;
} }
} }
} }
else else
{ {
foreach (var tr in listContainerTransform) foreach (var tr in _listContainerTransform)
{ {
GameObject child = ((Transform)tr).gameObject; GameObject child = ((Transform)tr).gameObject;
if (child.activeInHierarchy) if (child.activeInHierarchy)
@ -197,7 +202,7 @@ namespace UnityEngine.UI.Extensions
childLayout = child.AddComponent<LayoutElement>(); childLayout = child.AddComponent<LayoutElement>();
} }
childLayout.minHeight = itemSize; childLayout.minHeight = _itemSize;
} }
} }
} }
@ -206,12 +211,12 @@ namespace UnityEngine.UI.Extensions
public void UpdateListItemPositions() public void UpdateListItemPositions()
{ {
if (!listContainerRectTransform.rect.size.Equals(listContainerCachedSize)) if (!_listContainerRectTransform.rect.size.Equals(_listContainerCachedSize))
{ {
// checking how many children of list are active // checking how many children of list are active
int activeCount = 0; int activeCount = 0;
foreach (var tr in listContainerTransform) foreach (var tr in _listContainerTransform)
{ {
if (((Transform)tr).gameObject.activeInHierarchy) if (((Transform)tr).gameObject.activeInHierarchy)
{ {
@ -220,29 +225,29 @@ namespace UnityEngine.UI.Extensions
} }
// if anything changed since last check reinitialize anchors list // if anything changed since last check reinitialize anchors list
itemsCount = 0; _itemsCount = 0;
Array.Resize(ref pageAnchorPositions, activeCount); Array.Resize(ref _pageAnchorPositions, activeCount);
if (activeCount > 0) if (activeCount > 0)
{ {
pages = Mathf.Max(activeCount - itemsVisibleAtOnce + 1, 1); _pages = Mathf.Max(activeCount - ItemsVisibleAtOnce + 1, 1);
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
// looking for list spanning range min/max // looking for list spanning range min/max
scrollRect.horizontalNormalizedPosition = 0; _scroll_rect.horizontalNormalizedPosition = 0;
listContainerMaxPosition = listContainerTransform.localPosition.x; _listContainerMaxPosition = _listContainerTransform.localPosition.x;
scrollRect.horizontalNormalizedPosition = 1; _scroll_rect.horizontalNormalizedPosition = 1;
listContainerMinPosition = listContainerTransform.localPosition.x; _listContainerMinPosition = _listContainerTransform.localPosition.x;
listContainerSize = listContainerMaxPosition - listContainerMinPosition; _listContainerSize = _listContainerMaxPosition - _listContainerMinPosition;
for (var i = 0; i < pages; i++) for (var i = 0; i < _pages; i++)
{ {
pageAnchorPositions[i] = new Vector3( _pageAnchorPositions[i] = new Vector3(
listContainerMaxPosition - itemSize * i, _listContainerMaxPosition - _itemSize * i,
listContainerTransform.localPosition.y, _listContainerTransform.localPosition.y,
listContainerTransform.localPosition.z _listContainerTransform.localPosition.z
); );
} }
} }
@ -250,35 +255,35 @@ namespace UnityEngine.UI.Extensions
{ {
//Debug.Log ("-------------looking for list spanning range----------------"); //Debug.Log ("-------------looking for list spanning range----------------");
// looking for list spanning range // looking for list spanning range
scrollRect.verticalNormalizedPosition = 1; _scroll_rect.verticalNormalizedPosition = 1;
listContainerMinPosition = listContainerTransform.localPosition.y; _listContainerMinPosition = _listContainerTransform.localPosition.y;
scrollRect.verticalNormalizedPosition = 0; _scroll_rect.verticalNormalizedPosition = 0;
listContainerMaxPosition = listContainerTransform.localPosition.y; _listContainerMaxPosition = _listContainerTransform.localPosition.y;
listContainerSize = listContainerMaxPosition - listContainerMinPosition; _listContainerSize = _listContainerMaxPosition - _listContainerMinPosition;
for (var i = 0; i < pages; i++) for (var i = 0; i < _pages; i++)
{ {
pageAnchorPositions[i] = new Vector3( _pageAnchorPositions[i] = new Vector3(
listContainerTransform.localPosition.x, _listContainerTransform.localPosition.x,
listContainerMinPosition + itemSize * i, _listContainerMinPosition + _itemSize * i,
listContainerTransform.localPosition.z _listContainerTransform.localPosition.z
); );
} }
} }
UpdateScrollbar(linkScrolbarSteps); UpdateScrollbar(LinkScrolbarSteps);
startingPage = Mathf.Min(startingPage, pages); _startingPage = Mathf.Min(_startingPage, _pages);
ResetPage(); ResetPage();
} }
if (itemsCount != activeCount) if (_itemsCount != activeCount)
{ {
PageChanged(CurrentPage()); PageChanged(CurrentPage());
} }
itemsCount = activeCount; _itemsCount = activeCount;
listContainerCachedSize.Set(listContainerRectTransform.rect.size.x, listContainerRectTransform.rect.size.y); _listContainerCachedSize.Set(_listContainerRectTransform.rect.size.x, _listContainerRectTransform.rect.size.y);
} }
} }
@ -287,30 +292,30 @@ namespace UnityEngine.UI.Extensions
{ {
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
scrollRect.horizontalNormalizedPosition = pages > 1 ? (float)startingPage / (float)(pages - 1) : 0; _scroll_rect.horizontalNormalizedPosition = _pages > 1 ? (float)_startingPage / (float)(_pages - 1) : 0;
} }
else else
{ {
scrollRect.verticalNormalizedPosition = pages > 1 ? (float)(pages - startingPage - 1) / (float)(pages - 1) : 0; _scroll_rect.verticalNormalizedPosition = _pages > 1 ? (float)(_pages - _startingPage - 1) / (float)(_pages - 1) : 0;
} }
} }
protected void UpdateScrollbar(bool linkSteps) private void UpdateScrollbar(bool linkSteps)
{ {
if (linkSteps) if (linkSteps)
{ {
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
if (scrollRect.horizontalScrollbar != null) if (_scroll_rect.horizontalScrollbar != null)
{ {
scrollRect.horizontalScrollbar.numberOfSteps = pages; _scroll_rect.horizontalScrollbar.numberOfSteps = _pages;
} }
} }
else else
{ {
if (scrollRect.verticalScrollbar != null) if (_scroll_rect.verticalScrollbar != null)
{ {
scrollRect.verticalScrollbar.numberOfSteps = pages; _scroll_rect.verticalScrollbar.numberOfSteps = _pages;
} }
} }
} }
@ -318,16 +323,16 @@ namespace UnityEngine.UI.Extensions
{ {
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
if (scrollRect.horizontalScrollbar != null) if (_scroll_rect.horizontalScrollbar != null)
{ {
scrollRect.horizontalScrollbar.numberOfSteps = 0; _scroll_rect.horizontalScrollbar.numberOfSteps = 0;
} }
} }
else else
{ {
if (scrollRect.verticalScrollbar != null) if (_scroll_rect.verticalScrollbar != null)
{ {
scrollRect.verticalScrollbar.numberOfSteps = 0; _scroll_rect.verticalScrollbar.numberOfSteps = 0;
} }
} }
} }
@ -338,30 +343,30 @@ namespace UnityEngine.UI.Extensions
UpdateListItemsSize(); UpdateListItemsSize();
UpdateListItemPositions(); UpdateListItemPositions();
if (lerp) if (_lerp)
{ {
UpdateScrollbar(false); UpdateScrollbar(false);
listContainerTransform.localPosition = Vector3.Lerp(listContainerTransform.localPosition, lerpTarget, 7.5f * Time.deltaTime); _listContainerTransform.localPosition = Vector3.Lerp(_listContainerTransform.localPosition, _lerpTarget, 7.5f * Time.deltaTime);
if (Vector3.Distance(listContainerTransform.localPosition, lerpTarget) < 0.001f) if (Vector3.Distance(_listContainerTransform.localPosition, _lerpTarget) < 0.001f)
{ {
listContainerTransform.localPosition = lerpTarget; _listContainerTransform.localPosition = _lerpTarget;
lerp = false; _lerp = false;
UpdateScrollbar(linkScrolbarSteps); UpdateScrollbar(LinkScrolbarSteps);
} }
//change the info bullets at the bottom of the screen. Just for visual effect //change the info bullets at the bottom of the screen. Just for visual effect
if (Vector3.Distance(listContainerTransform.localPosition, lerpTarget) < 10f) if (Vector3.Distance(_listContainerTransform.localPosition, _lerpTarget) < 10f)
{ {
PageChanged(CurrentPage()); PageChanged(CurrentPage());
} }
} }
if (fastSwipeTimer) if (_fastSwipeTimer)
{ {
fastSwipeCounter++; _fastSwipeCounter++;
} }
} }
@ -373,10 +378,10 @@ namespace UnityEngine.UI.Extensions
{ {
UpdateListItemPositions(); UpdateListItemPositions();
if (CurrentPage() < pages - 1) if (CurrentPage() < _pages - 1)
{ {
lerp = true; _lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage() + 1]; _lerpTarget = _pageAnchorPositions[CurrentPage() + 1];
PageChanged(CurrentPage() + 1); PageChanged(CurrentPage() + 1);
} }
@ -389,8 +394,8 @@ namespace UnityEngine.UI.Extensions
if (CurrentPage() > 0) if (CurrentPage() > 0)
{ {
lerp = true; _lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage() - 1]; _lerpTarget = _pageAnchorPositions[CurrentPage() - 1];
PageChanged(CurrentPage() - 1); PageChanged(CurrentPage() - 1);
} }
@ -399,12 +404,12 @@ namespace UnityEngine.UI.Extensions
//Because the CurrentScreen function is not so reliable, these are the functions used for swipes //Because the CurrentScreen function is not so reliable, these are the functions used for swipes
private void NextScreenCommand() private void NextScreenCommand()
{ {
if (pageOnDragStart < pages - 1) if (_pageOnDragStart < _pages - 1)
{ {
int targetPage = Mathf.Min(pages - 1, pageOnDragStart + itemsVisibleAtOnce); int targetPage = Mathf.Min(_pages - 1, _pageOnDragStart + ItemsVisibleAtOnce);
lerp = true; _lerp = true;
lerpTarget = pageAnchorPositions[targetPage]; _lerpTarget = _pageAnchorPositions[targetPage];
PageChanged(targetPage); PageChanged(targetPage);
} }
@ -413,12 +418,12 @@ namespace UnityEngine.UI.Extensions
//Because the CurrentScreen function is not so reliable, these are the functions used for swipes //Because the CurrentScreen function is not so reliable, these are the functions used for swipes
private void PrevScreenCommand() private void PrevScreenCommand()
{ {
if (pageOnDragStart > 0) if (_pageOnDragStart > 0)
{ {
int targetPage = Mathf.Max(0, pageOnDragStart - itemsVisibleAtOnce); int targetPage = Mathf.Max(0, _pageOnDragStart - ItemsVisibleAtOnce);
lerp = true; _lerp = true;
lerpTarget = pageAnchorPositions[targetPage]; _lerpTarget = _pageAnchorPositions[targetPage];
PageChanged(targetPage); PageChanged(targetPage);
} }
@ -432,27 +437,35 @@ namespace UnityEngine.UI.Extensions
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
pos = listContainerMaxPosition - listContainerTransform.localPosition.x; pos = _listContainerMaxPosition - _listContainerTransform.localPosition.x;
pos = Mathf.Clamp(pos, 0, listContainerSize); pos = Mathf.Clamp(pos, 0, _listContainerSize);
} }
else else
{ {
pos = listContainerTransform.localPosition.y - listContainerMinPosition; pos = _listContainerTransform.localPosition.y - _listContainerMinPosition;
pos = Mathf.Clamp(pos, 0, listContainerSize); pos = Mathf.Clamp(pos, 0, _listContainerSize);
} }
float page = pos / itemSize; float page = pos / _itemSize;
return Mathf.Clamp(Mathf.RoundToInt(page), 0, pages); return Mathf.Clamp(Mathf.RoundToInt(page), 0, _pages);
}
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
public void SetLerp(bool value)
{
_lerp = value;
} }
public void ChangePage(int page) public void ChangePage(int page)
{ {
if (0 <= page && page < pages) if (0 <= page && page < _pages)
{ {
lerp = true; _lerp = true;
lerpTarget = pageAnchorPositions[page]; _lerpTarget = _pageAnchorPositions[page];
PageChanged(page); PageChanged(page);
} }
@ -461,16 +474,16 @@ namespace UnityEngine.UI.Extensions
//changes the bullets on the bottom of the page - pagination //changes the bullets on the bottom of the page - pagination
private void PageChanged(int currentPage) private void PageChanged(int currentPage)
{ {
startingPage = currentPage; _startingPage = currentPage;
if (nextButton) if (NextButton)
{ {
nextButton.interactable = currentPage < pages - 1; NextButton.interactable = currentPage < _pages - 1;
} }
if (prevButton) if (PrevButton)
{ {
prevButton.interactable = currentPage > 0; PrevButton.interactable = currentPage > 0;
} }
if (onPageChange != null) if (onPageChange != null)
@ -484,35 +497,35 @@ namespace UnityEngine.UI.Extensions
{ {
UpdateScrollbar(false); UpdateScrollbar(false);
fastSwipeCounter = 0; _fastSwipeCounter = 0;
fastSwipeTimer = true; _fastSwipeTimer = true;
positionOnDragStart = eventData.position; _positionOnDragStart = eventData.position;
pageOnDragStart = CurrentPage(); _pageOnDragStart = CurrentPage();
} }
public void OnEndDrag(PointerEventData eventData) public void OnEndDrag(PointerEventData eventData)
{ {
startDrag = true; _startDrag = true;
float change = 0; float change = 0;
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
change = positionOnDragStart.x - eventData.position.x; change = _positionOnDragStart.x - eventData.position.x;
} }
else else
{ {
change = -positionOnDragStart.y + eventData.position.y; change = -_positionOnDragStart.y + eventData.position.y;
} }
if (useFastSwipe) if (UseFastSwipe)
{ {
fastSwipe = false; fastSwipe = false;
fastSwipeTimer = false; _fastSwipeTimer = false;
if (fastSwipeCounter <= fastSwipeTarget) if (_fastSwipeCounter <= _fastSwipeTarget)
{ {
if (Math.Abs(change) > fastSwipeThreshold) if (Math.Abs(change) > FastSwipeThreshold)
{ {
fastSwipe = true; fastSwipe = true;
} }
@ -530,27 +543,29 @@ namespace UnityEngine.UI.Extensions
} }
else else
{ {
lerp = true; _lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage()]; _lerpTarget = _pageAnchorPositions[CurrentPage()];
} }
} }
else else
{ {
lerp = true; _lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage()]; _lerpTarget = _pageAnchorPositions[CurrentPage()];
} }
} }
public void OnDrag(PointerEventData eventData) public void OnDrag(PointerEventData eventData)
{ {
lerp = false; _lerp = false;
if (startDrag) if (_startDrag)
{ {
OnBeginDrag(eventData); OnBeginDrag(eventData);
startDrag = false; _startDrag = false;
} }
} }
public void StartScreenChange() { }
#endregion #endregion
} }
} }

View File

@ -1,94 +1,99 @@
using System; /// Credit BinaryX
/// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-2#post-1945602
/// Updated by ddreaper - removed dependency on a custom ScrollRect script. Now implements drag interfaces and standard Scroll Rect.
using System;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
public class ScrollSnapBase : MonoBehaviour, IBeginDragHandler, IDragHandler public class ScrollSnapBase : MonoBehaviour, IBeginDragHandler, IDragHandler, IScrollSnap
{ {
internal RectTransform _screensContainer; internal RectTransform _screensContainer;
internal bool _isVertical; internal bool _isVertical;
internal int _screens = 1; internal int _screens = 1;
internal float _scrollStartPosition; internal float _scrollStartPosition;
internal float _childSize; internal float _childSize;
private float _childPos, _maskSize; private float _childPos, _maskSize;
internal Vector2 _childAnchorPoint; internal Vector2 _childAnchorPoint;
internal ScrollRect _scroll_rect; internal ScrollRect _scroll_rect;
internal Vector3 _lerp_target; internal Vector3 _lerp_target;
internal bool _lerp; internal bool _lerp;
internal bool _pointerDown = false; internal bool _pointerDown = false;
internal bool _settled = true; internal bool _settled = true;
internal Vector3 _startPosition = new Vector3(); internal Vector3 _startPosition = new Vector3();
[Tooltip("The currently active page")] [Tooltip("The currently active page")]
internal int _currentPage; internal int _currentPage;
internal int _previousPage; internal int _previousPage;
internal int _halfNoVisibleItems; internal int _halfNoVisibleItems;
private int _bottomItem, _topItem; private int _bottomItem, _topItem;
private bool _moveStarted;
[Serializable] [Serializable]
public class SelectionChangeStartEvent : UnityEvent { } public class SelectionChangeStartEvent : UnityEvent { }
[Serializable] [Serializable]
public class SelectionPageChangedEvent : UnityEvent<int> { } public class SelectionPageChangedEvent : UnityEvent<int> { }
[Serializable] [Serializable]
public class SelectionChangeEndEvent : UnityEvent<int> { } public class SelectionChangeEndEvent : UnityEvent<int> { }
[Tooltip("The screen / page to start the control on\n*Note, this is a 0 indexed array")] [Tooltip("The screen / page to start the control on\n*Note, this is a 0 indexed array")]
[SerializeField] [SerializeField]
public int StartingScreen = 0; public int StartingScreen = 0;
[Tooltip("The distance between two pages based on page height, by default pages are next to each other")] [Tooltip("The distance between two pages based on page height, by default pages are next to each other")]
[SerializeField] [SerializeField]
[Range(0, 8)] [Range(0, 8)]
public float PageStep = 1; public float PageStep = 1;
[Tooltip("The gameobject that contains toggles which suggest pagination. (optional)")] [Tooltip("The gameobject that contains toggles which suggest pagination. (optional)")]
public GameObject Pagination; public GameObject Pagination;
[Tooltip("Button to go to the previous page. (optional)")] [Tooltip("Button to go to the previous page. (optional)")]
public GameObject PrevButton; public GameObject PrevButton;
[Tooltip("Button to go to the next page. (optional)")] [Tooltip("Button to go to the next page. (optional)")]
public GameObject NextButton; public GameObject NextButton;
[Tooltip("Transition speed between pages. (optional)")] [Tooltip("Transition speed between pages. (optional)")]
public float transitionSpeed = 7.5f; public float transitionSpeed = 7.5f;
[Tooltip("Fast Swipe makes swiping page next / previous (optional)")] [Tooltip("Fast Swipe makes swiping page next / previous (optional)")]
public Boolean UseFastSwipe = false; public Boolean UseFastSwipe = false;
[Tooltip("How far swipe has to travel to initiate a page change (optional)")] [Tooltip("How far swipe has to travel to initiate a page change (optional)")]
public int FastSwipeThreshold = 100; public int FastSwipeThreshold = 100;
[Tooltip("Speed at which the ScrollRect will keep scrolling before slowing down and stopping (optional)")] [Tooltip("Speed at which the ScrollRect will keep scrolling before slowing down and stopping (optional)")]
public int SwipeVelocityThreshold = 200; public int SwipeVelocityThreshold = 100;
[Tooltip("The visible bounds area, controls which items are visible/enabled. *Note Should use a RectMask. (optional)")] [Tooltip("The visible bounds area, controls which items are visible/enabled. *Note Should use a RectMask. (optional)")]
public RectTransform MaskArea; public RectTransform MaskArea;
[Tooltip("Pixel size to buffer arround Mask Area. (optional)")] [Tooltip("Pixel size to buffer arround Mask Area. (optional)")]
public float MaskBuffer = 1; public float MaskBuffer = 1;
public int CurrentPage public int CurrentPage
{ {
get get
{ {
return _currentPage; return _currentPage;
} }
internal set internal set
{ {
if ((value != _currentPage && value >= 0 && value < _screensContainer.childCount) || (value == 0 && _screensContainer.childCount == 0)) if ((value != _currentPage && value >= 0 && value < _screensContainer.childCount) || (value == 0 && _screensContainer.childCount == 0))
{ {
_previousPage = _currentPage; _previousPage = _currentPage;
_currentPage = value; _currentPage = value;
if(MaskArea) UpdateVisible(); if (MaskArea) UpdateVisible();
if(!_lerp) ScreenChange(); if (!_lerp) ScreenChange();
OnCurrentScreenChange(_currentPage); OnCurrentScreenChange(_currentPage);
} }
} }
} }
[Tooltip("By default the container will lerp to the start when enabled in the scene, this option overrides this and forces it to simply jump without lerping")] [Tooltip("By default the container will lerp to the start when enabled in the scene, this option overrides this and forces it to simply jump without lerping")]
public bool JumpOnEnable = false; public bool JumpOnEnable = false;
@ -97,51 +102,59 @@ namespace UnityEngine.UI.Extensions
public bool RestartOnEnable = false; public bool RestartOnEnable = false;
[Tooltip("(Experimental)\nBy default, child array objects will use the parent transform\nHowever you can disable this for some interesting effects")] [Tooltip("(Experimental)\nBy default, child array objects will use the parent transform\nHowever you can disable this for some interesting effects")]
public bool UseParentTransform = true; public bool UseParentTransform = true;
[Tooltip("Scroll Snap children. (optional)\nEither place objects in the scene as children OR\nPrefabs in this array, NOT BOTH")] [Tooltip("Scroll Snap children. (optional)\nEither place objects in the scene as children OR\nPrefabs in this array, NOT BOTH")]
public GameObject[] ChildObjects; public GameObject[] ChildObjects;
[SerializeField] [SerializeField]
[Tooltip("Event fires when a user starts to change the selection")] [Tooltip("Event fires when a user starts to change the selection")]
private SelectionChangeStartEvent m_OnSelectionChangeStartEvent = new SelectionChangeStartEvent(); private SelectionChangeStartEvent m_OnSelectionChangeStartEvent = new SelectionChangeStartEvent();
public SelectionChangeStartEvent OnSelectionChangeStartEvent { get { return m_OnSelectionChangeStartEvent; } set { m_OnSelectionChangeStartEvent = value; } } public SelectionChangeStartEvent OnSelectionChangeStartEvent { get { return m_OnSelectionChangeStartEvent; } set { m_OnSelectionChangeStartEvent = value; } }
[SerializeField] [SerializeField]
[Tooltip("Event fires as the page changes, while dragging or jumping")] [Tooltip("Event fires as the page changes, while dragging or jumping")]
private SelectionPageChangedEvent m_OnSelectionPageChangedEvent = new SelectionPageChangedEvent(); private SelectionPageChangedEvent m_OnSelectionPageChangedEvent = new SelectionPageChangedEvent();
public SelectionPageChangedEvent OnSelectionPageChangedEvent { get { return m_OnSelectionPageChangedEvent; } set { m_OnSelectionPageChangedEvent = value; } } public SelectionPageChangedEvent OnSelectionPageChangedEvent { get { return m_OnSelectionPageChangedEvent; } set { m_OnSelectionPageChangedEvent = value; } }
[SerializeField] [SerializeField]
[Tooltip("Event fires when the page settles after a user has dragged")] [Tooltip("Event fires when the page settles after a user has dragged")]
private SelectionChangeEndEvent m_OnSelectionChangeEndEvent = new SelectionChangeEndEvent(); private SelectionChangeEndEvent m_OnSelectionChangeEndEvent = new SelectionChangeEndEvent();
public SelectionChangeEndEvent OnSelectionChangeEndEvent { get { return m_OnSelectionChangeEndEvent; } set { m_OnSelectionChangeEndEvent = value; } } public SelectionChangeEndEvent OnSelectionChangeEndEvent { get { return m_OnSelectionChangeEndEvent; } set { m_OnSelectionChangeEndEvent = value; } }
// Use this for initialization // Use this for initialization
void Awake() void Awake()
{ {
_scroll_rect = gameObject.GetComponent<ScrollRect>(); if (_scroll_rect == null)
{
_scroll_rect = gameObject.GetComponent<ScrollRect>();
}
if (_scroll_rect.horizontalScrollbar && _scroll_rect.horizontal)
{
var hscroll = _scroll_rect.horizontalScrollbar.gameObject.AddComponent<ScrollSnapScrollbarHelper>();
hscroll.ss = this;
}
if (_scroll_rect.verticalScrollbar && _scroll_rect.vertical)
{
var vscroll = _scroll_rect.verticalScrollbar.gameObject.AddComponent<ScrollSnapScrollbarHelper>();
vscroll.ss = this;
}
if (_scroll_rect.horizontalScrollbar || _scroll_rect.verticalScrollbar) if (StartingScreen < 0)
{ {
Debug.LogWarning("Warning, using scrollbars with the Scroll Snap controls is not advised as it causes unpredictable results"); StartingScreen = 0;
} }
if (StartingScreen < 0) _screensContainer = _scroll_rect.content;
{
StartingScreen = 0;
}
_screensContainer = _scroll_rect.content; InitialiseChildObjects();
InitialiseChildObjects(); if (NextButton)
NextButton.GetComponent<Button>().onClick.AddListener(() => { NextScreen(); });
if (NextButton) if (PrevButton)
NextButton.GetComponent<Button>().onClick.AddListener(() => { NextScreen(); }); PrevButton.GetComponent<Button>().onClick.AddListener(() => { PreviousScreen(); });
}
if (PrevButton)
PrevButton.GetComponent<Button>().onClick.AddListener(() => { PreviousScreen(); });
}
internal void InitialiseChildObjects() internal void InitialiseChildObjects()
{ {
@ -162,306 +175,354 @@ namespace UnityEngine.UI.Extensions
} }
internal void InitialiseChildObjectsFromScene() internal void InitialiseChildObjectsFromScene()
{ {
int childCount = _screensContainer.childCount; int childCount = _screensContainer.childCount;
ChildObjects = new GameObject[childCount]; ChildObjects = new GameObject[childCount];
for (int i = 0; i < childCount; i++) for (int i = 0; i < childCount; i++)
{ {
ChildObjects[i] = _screensContainer.transform.GetChild(i).gameObject; ChildObjects[i] = _screensContainer.transform.GetChild(i).gameObject;
if (MaskArea && ChildObjects[i].activeSelf) if (MaskArea && ChildObjects[i].activeSelf)
{ {
ChildObjects[i].SetActive(false); ChildObjects[i].SetActive(false);
} }
} }
} }
internal void InitialiseChildObjectsFromArray() internal void InitialiseChildObjectsFromArray()
{ {
int childCount = ChildObjects.Length; int childCount = ChildObjects.Length;
RectTransform childRect; RectTransform childRect;
GameObject child; GameObject child;
for (int i = 0; i < childCount; i++) for (int i = 0; i < childCount; i++)
{ {
child = GameObject.Instantiate(ChildObjects[i]); child = GameObject.Instantiate(ChildObjects[i]);
//Optionally, use original GO transform when initialising, by default will use parent RectTransform position/rotation //Optionally, use original GO transform when initialising, by default will use parent RectTransform position/rotation
if (UseParentTransform) if (UseParentTransform)
{ {
childRect = child.GetComponent<RectTransform>(); childRect = child.GetComponent<RectTransform>();
childRect.rotation = _screensContainer.rotation; childRect.rotation = _screensContainer.rotation;
childRect.localScale = _screensContainer.localScale; childRect.localScale = _screensContainer.localScale;
childRect.position = _screensContainer.position; childRect.position = _screensContainer.position;
} }
child.transform.SetParent(_screensContainer.transform); child.transform.SetParent(_screensContainer.transform);
ChildObjects[i] = child; ChildObjects[i] = child;
if (MaskArea && ChildObjects[i].activeSelf) if (MaskArea && ChildObjects[i].activeSelf)
{ {
ChildObjects[i].SetActive(false); ChildObjects[i].SetActive(false);
} }
} }
} }
internal void UpdateVisible() internal void UpdateVisible()
{ {
//If there are no objects in the scene or a mask, exit //If there are no objects in the scene or a mask, exit
if (!MaskArea || ChildObjects == null || ChildObjects.Length < 1 || _screensContainer.childCount < 1) if (!MaskArea || ChildObjects == null || ChildObjects.Length < 1 || _screensContainer.childCount < 1)
{ {
return; return;
} }
_maskSize = _isVertical ? MaskArea.rect.height : MaskArea.rect.width; _maskSize = _isVertical ? MaskArea.rect.height : MaskArea.rect.width;
_halfNoVisibleItems = (int)Math.Round(_maskSize / (_childSize * MaskBuffer), MidpointRounding.AwayFromZero) / 2; _halfNoVisibleItems = (int)Math.Round(_maskSize / (_childSize * MaskBuffer), MidpointRounding.AwayFromZero) / 2;
_bottomItem = _topItem = 0; _bottomItem = _topItem = 0;
//work out how many items below the current page can be visible //work out how many items below the current page can be visible
for (int i = _halfNoVisibleItems + 1; i > 0; i--) for (int i = _halfNoVisibleItems + 1; i > 0; i--)
{ {
_bottomItem = _currentPage - i < 0 ? 0 : i; _bottomItem = _currentPage - i < 0 ? 0 : i;
if (_bottomItem > 0) break; if (_bottomItem > 0) break;
} }
//work out how many items above the current page can be visible //work out how many items above the current page can be visible
for (int i = _halfNoVisibleItems + 1; i > 0; i--) for (int i = _halfNoVisibleItems + 1; i > 0; i--)
{ {
_topItem = _screensContainer.childCount - _currentPage - i < 0 ? 0 : i; _topItem = _screensContainer.childCount - _currentPage - i < 0 ? 0 : i;
if (_topItem > 0) break; if (_topItem > 0) break;
} }
//Set the active items active //Set the active items active
for (int i = CurrentPage - _bottomItem; i < CurrentPage + _topItem; i++) for (int i = CurrentPage - _bottomItem; i < CurrentPage + _topItem; i++)
{ {
try try
{ {
ChildObjects[i].SetActive(true); ChildObjects[i].SetActive(true);
} }
catch catch
{ {
Debug.Log("Failed to setactive child [" + i + "]"); Debug.Log("Failed to setactive child [" + i + "]");
} }
} }
//Deactivate items out of visibility at the bottom of the ScrollRect Mask (only on scroll) //Deactivate items out of visibility at the bottom of the ScrollRect Mask (only on scroll)
if (_currentPage > _halfNoVisibleItems) ChildObjects[CurrentPage - _bottomItem].SetActive(false); if (_currentPage > _halfNoVisibleItems) ChildObjects[CurrentPage - _bottomItem].SetActive(false);
//Deactivate items out of visibility at the top of the ScrollRect Mask (only on scroll) //Deactivate items out of visibility at the top of the ScrollRect Mask (only on scroll)
if (_screensContainer.childCount - _currentPage > _topItem) ChildObjects[CurrentPage + _topItem].SetActive(false); if (_screensContainer.childCount - _currentPage > _topItem) ChildObjects[CurrentPage + _topItem].SetActive(false);
} }
//Function for switching screens with buttons //Function for switching screens with buttons
public void NextScreen() public void NextScreen()
{ {
if (_currentPage < _screens - 1) if (_currentPage < _screens - 1)
{ {
if (!_lerp) StartScreenChange(); if (!_lerp) StartScreenChange();
_lerp = true; _lerp = true;
CurrentPage = _currentPage + 1; CurrentPage = _currentPage + 1;
GetPositionforPage(_currentPage, ref _lerp_target); GetPositionforPage(_currentPage, ref _lerp_target);
ScreenChange(); ScreenChange();
} }
} }
//Function for switching screens with buttons //Function for switching screens with buttons
public void PreviousScreen() public void PreviousScreen()
{ {
if (_currentPage > 0) if (_currentPage > 0)
{ {
if (!_lerp) StartScreenChange(); if (!_lerp) StartScreenChange();
_lerp = true; _lerp = true;
CurrentPage = _currentPage - 1; CurrentPage = _currentPage - 1;
GetPositionforPage(_currentPage, ref _lerp_target); GetPositionforPage(_currentPage, ref _lerp_target);
ScreenChange(); ScreenChange();
} }
} }
/// <summary> /// <summary>
/// Function for switching to a specific screen /// Function for switching to a specific screen
/// *Note, this is based on a 0 starting index - 0 to x /// *Note, this is based on a 0 starting index - 0 to x
/// </summary> /// </summary>
/// <param name="screenIndex">0 starting index of page to jump to</param> /// <param name="screenIndex">0 starting index of page to jump to</param>
public void GoToScreen(int screenIndex) public void GoToScreen(int screenIndex)
{ {
if (screenIndex <= _screens - 1 && screenIndex >= 0) if (screenIndex <= _screens - 1 && screenIndex >= 0)
{ {
if (!_lerp) StartScreenChange(); if (!_lerp) StartScreenChange();
_lerp = true; _lerp = true;
CurrentPage = screenIndex; CurrentPage = screenIndex;
GetPositionforPage(_currentPage, ref _lerp_target); GetPositionforPage(_currentPage, ref _lerp_target);
ScreenChange(); ScreenChange();
} }
} }
/// <summary> /// <summary>
/// Gets the closest page for the current Scroll Rect container position /// Gets the closest page for the current Scroll Rect container position
/// </summary> /// </summary>
/// <param name="pos">Position to test, normally the Scroll Rect container Local position</param> /// <param name="pos">Position to test, normally the Scroll Rect container Local position</param>
/// <returns>Closest Page number (zero indexed array value)</returns> /// <returns>Closest Page number (zero indexed array value)</returns>
internal int GetPageforPosition(Vector3 pos) internal int GetPageforPosition(Vector3 pos)
{ {
return _isVertical ? return _isVertical ?
-(int)Math.Round((pos.y - _scrollStartPosition) / _childSize) : -(int)Math.Round((pos.y - _scrollStartPosition) / _childSize) :
-(int)Math.Round((pos.x - _scrollStartPosition) / _childSize); -(int)Math.Round((pos.x - _scrollStartPosition) / _childSize);
} }
/// <summary> /// <summary>
/// Validates if the current Scroll Rect container position is within the bounds for a page /// Validates if the current Scroll Rect container position is within the bounds for a page
/// </summary> /// </summary>
/// <param name="pos">Position to test, normally the Scroll Rect container Local position</param> /// <param name="pos">Position to test, normally the Scroll Rect container Local position</param>
/// <returns>True / False, is the position in the bounds of a page</returns> /// <returns>True / False, is the position in the bounds of a page</returns>
internal bool IsRectSettledOnaPage(Vector3 pos) internal bool IsRectSettledOnaPage(Vector3 pos)
{ {
return _isVertical ? return _isVertical ?
-((pos.y - _scrollStartPosition) / _childSize) == -(int)Math.Round((pos.y - _scrollStartPosition) / _childSize) : -((pos.y - _scrollStartPosition) / _childSize) == -(int)Math.Round((pos.y - _scrollStartPosition) / _childSize) :
-((pos.x - _scrollStartPosition) / _childSize) == -(int)Math.Round((pos.x - _scrollStartPosition) / _childSize); -((pos.x - _scrollStartPosition) / _childSize) == -(int)Math.Round((pos.x - _scrollStartPosition) / _childSize);
} }
/// <summary> /// <summary>
/// Returns the local position for a child page based on the required page number /// Returns the local position for a child page based on the required page number
/// </summary> /// </summary>
/// <param name="page">Page that the position is required for (Zero indexed array value)</param> /// <param name="page">Page that the position is required for (Zero indexed array value)</param>
/// <param name="target">Outputs the local position for the selected page</param> /// <param name="target">Outputs the local position for the selected page</param>
internal void GetPositionforPage(int page, ref Vector3 target) internal void GetPositionforPage(int page, ref Vector3 target)
{ {
_childPos = -_childSize * page; _childPos = -_childSize * page;
if (_isVertical) if (_isVertical)
{ {
target.y = _childPos + _scrollStartPosition; target.y = _childPos + _scrollStartPosition;
} }
else else
{ {
target.x = _childPos + _scrollStartPosition; target.x = _childPos + _scrollStartPosition;
} }
} }
/// <summary> /// <summary>
/// Updates the _Lerp target to the closest page and updates the pagination bullets. Each control's update loop will then handle the move. /// Updates the _Lerp target to the closest page and updates the pagination bullets. Each control's update loop will then handle the move.
/// </summary> /// </summary>
internal void ScrollToClosestElement() internal void ScrollToClosestElement()
{ {
_lerp = true; _lerp = true;
CurrentPage = GetPageforPosition(_screensContainer.localPosition); CurrentPage = GetPageforPosition(_screensContainer.localPosition);
GetPositionforPage(_currentPage, ref _lerp_target); GetPositionforPage(_currentPage, ref _lerp_target);
OnCurrentScreenChange(_currentPage); OnCurrentScreenChange(_currentPage);
} }
/// <summary> /// <summary>
/// notifies pagination indicator and navigation buttons of a screen change /// notifies pagination indicator and navigation buttons of a screen change
/// </summary> /// </summary>
internal void OnCurrentScreenChange(int currentScreen) internal void OnCurrentScreenChange(int currentScreen)
{ {
ChangeBulletsInfo(currentScreen); ChangeBulletsInfo(currentScreen);
ToggleNavigationButtons(currentScreen); ToggleNavigationButtons(currentScreen);
} }
/// <summary> /// <summary>
/// changes the bullets on the bottom of the page - pagination /// changes the bullets on the bottom of the page - pagination
/// </summary> /// </summary>
/// <param name="targetScreen"></param> /// <param name="targetScreen"></param>
private void ChangeBulletsInfo(int targetScreen) private void ChangeBulletsInfo(int targetScreen)
{ {
if (Pagination) if (Pagination)
for (int i = 0; i < Pagination.transform.childCount; i++) for (int i = 0; i < Pagination.transform.childCount; i++)
{ {
Pagination.transform.GetChild(i).GetComponent<Toggle>().isOn = (targetScreen == i) Pagination.transform.GetChild(i).GetComponent<Toggle>().isOn = (targetScreen == i)
? true ? true
: false; : false;
} }
} }
/// <summary> /// <summary>
/// disables the page navigation buttons when at the first or last screen /// disables the page navigation buttons when at the first or last screen
/// </summary> /// </summary>
/// <param name="targetScreen"></param> /// <param name="targetScreen"></param>
private void ToggleNavigationButtons(int targetScreen) { private void ToggleNavigationButtons(int targetScreen)
if (PrevButton) { {
PrevButton.GetComponent<Button>().interactable = targetScreen > 0; if (PrevButton)
} {
PrevButton.GetComponent<Button>().interactable = targetScreen > 0;
}
if (NextButton) { if (NextButton)
NextButton.GetComponent<Button>().interactable = targetScreen < _screensContainer.transform.childCount - 1; {
} NextButton.GetComponent<Button>().interactable = targetScreen < _screensContainer.transform.childCount - 1;
} }
}
private void OnValidate() private void OnValidate()
{ {
var children = gameObject.GetComponent<ScrollRect>().content.childCount; if (_scroll_rect == null)
if (children != 0 || ChildObjects != null) {
{ _scroll_rect = GetComponent<ScrollRect>();
var childCount = ChildObjects == null || ChildObjects.Length == 0 ? children : ChildObjects.Length; }
if (StartingScreen > childCount - 1) if (!_scroll_rect.horizontal && !_scroll_rect.vertical)
{ {
StartingScreen = childCount - 1; Debug.LogError("ScrollRect has to have a direction, please select either Horizontal OR Vertical with the appropriate control.");
} }
if (_scroll_rect.horizontal && _scroll_rect.vertical)
{
Debug.LogError("ScrollRect has to be unidirectional, only use either Horizontal or Vertical on the ScrollRect, NOT both.");
}
if (StartingScreen < 0) var children = gameObject.GetComponent<ScrollRect>().content.childCount;
{ if (children != 0 || ChildObjects != null)
StartingScreen = 0; {
} var childCount = ChildObjects == null || ChildObjects.Length == 0 ? children : ChildObjects.Length;
} if (StartingScreen > childCount - 1)
{
StartingScreen = childCount - 1;
}
if (MaskBuffer <= 0) if (StartingScreen < 0)
{ {
MaskBuffer = 1; StartingScreen = 0;
} }
}
if (PageStep < 0) if (MaskBuffer <= 0)
{ {
PageStep = 0; MaskBuffer = 1;
} }
if (PageStep > 8) if (PageStep < 0)
{ {
PageStep = 9; PageStep = 0;
} }
}
/// <summary> if (PageStep > 8)
/// Event fires when the user starts to change the page, either via swipe or button. {
/// </summary> PageStep = 9;
internal void StartScreenChange() }
{ }
OnSelectionChangeStartEvent.Invoke();
}
/// <summary> /// <summary>
/// Event fires when the currently viewed page changes, also updates while the scroll is moving /// Event fires when the user starts to change the page, either via swipe or button.
/// </summary> /// </summary>
internal void ScreenChange() public void StartScreenChange()
{ {
OnSelectionPageChangedEvent.Invoke(_currentPage); if (!_moveStarted)
} {
_moveStarted = true;
OnSelectionChangeStartEvent.Invoke();
}
}
/// <summary> /// <summary>
/// Event fires when control settles on a page, outputs the new page number /// Event fires when the currently viewed page changes, also updates while the scroll is moving
/// </summary> /// </summary>
internal void EndScreenChange() internal void ScreenChange()
{ {
OnSelectionChangeEndEvent.Invoke(_currentPage); OnSelectionPageChangedEvent.Invoke(_currentPage);
_settled = true; }
}
#region Interfaces /// <summary>
/// <summary> /// Event fires when control settles on a page, outputs the new page number
/// Touch screen to start swiping /// </summary>
/// </summary> internal void EndScreenChange()
/// <param name="eventData"></param> {
public void OnBeginDrag(PointerEventData eventData) OnSelectionChangeEndEvent.Invoke(_currentPage);
{ _settled = true;
_pointerDown = true; _moveStarted = false;
_settled = false; }
StartScreenChange();
_startPosition = _screensContainer.localPosition;
}
/// <summary> #region Interfaces
/// While dragging do /// <summary>
/// </summary> /// Touch screen to start swiping
/// <param name="eventData"></param> /// </summary>
public void OnDrag(PointerEventData eventData) /// <param name="eventData"></param>
{ public void OnBeginDrag(PointerEventData eventData)
_lerp = false; {
} _pointerDown = true;
_settled = false;
StartScreenChange();
_startPosition = _screensContainer.localPosition;
}
#endregion /// <summary>
} /// While dragging do
/// </summary>
/// <param name="eventData"></param>
public void OnDrag(PointerEventData eventData)
{
_lerp = false;
}
#endregion
#region IScrollSnap Interface
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
int IScrollSnap.CurrentPage()
{
return CurrentPage = GetPageforPosition(_screensContainer.localPosition);
}
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
public void SetLerp(bool value)
{
_lerp = value;
}
/// <summary>
/// Added to provide a uniform interface for the ScrollBarHelper
/// </summary>
public void ChangePage(int page)
{
GoToScreen(page);
}
#endregion
}
} }

View File

@ -0,0 +1,54 @@
/// Credit Anonymous donation
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/120/horizontal-scroll-snap-scroll-bar-fix
/// Updated by ddreaper - Made extension support all types of scroll snap
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
public class ScrollSnapScrollbarHelper : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IBeginDragHandler, IEndDragHandler, IDragHandler
{
internal IScrollSnap ss;
public void OnBeginDrag(PointerEventData eventData)
{
OnScrollBarDown();
}
public void OnDrag(PointerEventData eventData)
{
ss.CurrentPage();
}
public void OnEndDrag(PointerEventData eventData)
{
OnScrollBarUp();
}
public void OnPointerDown(PointerEventData eventData)
{
OnScrollBarDown();
}
public void OnPointerUp(PointerEventData eventData)
{
OnScrollBarUp();
}
void OnScrollBarDown()
{
if (ss != null)
{
ss.SetLerp(false);
ss.StartScreenChange();
}
}
void OnScrollBarUp()
{
ss.SetLerp(true);
ss.ChangePage(ss.CurrentPage());
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2ec759101f5786b4b89aae8a1b47ca4b
timeCreated: 1498919960
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: