/// Credit setchi (https://github.com/setchi) /// Sourced from - https://github.com/setchi/FancyScrollView using System.Collections.Generic; namespace UnityEngine.UI.Extensions { public abstract class FancyScrollView : MonoBehaviour where TContext : class, new() { [SerializeField, Range(1e-2f, 1f)] protected float cellInterval = 0.2f; [SerializeField, Range(0f, 1f)] protected float scrollOffset = 0.5f; [SerializeField] protected bool loop = false; [SerializeField] protected Transform cellContainer = default; readonly IList> pool = new List>(); protected float currentPosition; protected abstract GameObject CellPrefab { get; } protected IList ItemsSource { get; set; } = new List(); protected TContext Context { get; } = new TContext(); /// /// Updates the contents. /// /// Items source. protected virtual void UpdateContents(IList itemsSource) { ItemsSource = itemsSource; Refresh(); } /// /// Refreshes the cells. /// protected virtual void Refresh() => UpdatePosition(currentPosition, true); /// /// Updates the scroll position. /// /// Position. protected virtual void UpdatePosition(float position) => UpdatePosition(position, false); protected void UpdatePosition(float position, bool forceRefresh) { currentPosition = position; var p = position - scrollOffset / cellInterval; var firstIndex = Mathf.CeilToInt(p); var firstPosition = (Mathf.Ceil(p) - p) * cellInterval; if (firstPosition + pool.Count * cellInterval < 1f) { ResizePool(firstPosition); } UpdateCells(firstPosition, firstIndex, forceRefresh); } void ResizePool(float firstPosition) { Debug.Assert(CellPrefab != null); Debug.Assert(cellContainer != null); var addCount = Mathf.CeilToInt((1f - firstPosition) / cellInterval) - pool.Count; for (var i = 0; i < addCount; i++) { var cell = Instantiate(CellPrefab, cellContainer).GetComponent>(); if (cell == null) { throw new MissingComponentException( $"FancyScrollViewCell<{typeof(TItemData).FullName}, {typeof(TContext).FullName}> " + $"component not found in {CellPrefab.name}."); } cell.SetupContext(Context); cell.SetVisible(false); pool.Add(cell); } } void UpdateCells(float firstPosition, int firstIndex, bool forceRefresh) { for (var i = 0; i < pool.Count; i++) { var index = firstIndex + i; var position = firstPosition + i * cellInterval; var cell = pool[CircularIndex(index, pool.Count)]; if (loop) { index = CircularIndex(index, ItemsSource.Count); } if (index < 0 || index >= ItemsSource.Count || position > 1f) { cell.SetVisible(false); continue; } if (forceRefresh || cell.Index != index || !cell.IsVisible) { cell.Index = index; cell.SetVisible(true); cell.UpdateContent(ItemsSource[index]); } cell.UpdatePosition(position); } } int CircularIndex(int i, int size) => size < 1 ? 0 : i < 0 ? size - 1 + (i + 1) % size : i % size; #if UNITY_EDITOR bool cachedLoop; float cachedCellInterval, cachedScrollOffset; void LateUpdate() { if (cachedLoop != loop || cachedCellInterval != cellInterval || cachedScrollOffset != scrollOffset) { cachedLoop = loop; cachedCellInterval = cellInterval; cachedScrollOffset = scrollOffset; UpdatePosition(currentPosition); } } #endif } public sealed class FancyScrollViewNullContext { } public abstract class FancyScrollView : FancyScrollView { } }