/// 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<TData, TContext> : MonoBehaviour where TContext : class { [SerializeField, Range(float.Epsilon, 1f)] float cellInterval; [SerializeField, Range(0f, 1f)] float cellOffset; [SerializeField] bool loop; [SerializeField] GameObject cellBase; [SerializeField] Transform cellContainer; readonly List<FancyScrollViewCell<TData, TContext>> cells = new List<FancyScrollViewCell<TData, TContext>>(); float currentPosition; protected List<TData> cellData = new List<TData>(); protected TContext Context { get; private set; } /// <summary> /// Sets the context. /// </summary> /// <param name="context">Context.</param> protected void SetContext(TContext context) { Context = context; for (int i = 0; i < cells.Count; i++) { cells[i].SetContext(context); } } /// <summary> /// Updates the contents. /// </summary> protected void UpdateContents() { UpdatePosition(currentPosition, true); } /// <summary> /// Updates the scroll position. /// </summary> /// <param name="position">Position.</param> /// <param name="forceUpdateContents">If set to <c>true</c> force update contents.</param> protected void UpdatePosition(float position, bool forceUpdateContents = false) { currentPosition = position; var visibleMinPosition = position - (cellOffset / cellInterval); var firstCellPosition = (Mathf.Ceil(visibleMinPosition) - visibleMinPosition) * cellInterval; var dataStartIndex = Mathf.CeilToInt(visibleMinPosition); var count = 0; for (float p = firstCellPosition; p <= 1f; p += cellInterval, count++) { if (count >= cells.Count) { cells.Add(CreateCell()); } } count = 0; for (float p = firstCellPosition; p <= 1f; p += cellInterval, count++) { var dataIndex = dataStartIndex + count; var cell = cells[GetCircularIndex(dataIndex, cells.Count)]; UpdateCell(cell, dataIndex, forceUpdateContents); if (cell.gameObject.activeSelf) { cell.UpdatePosition(p); } } while (count < cells.Count) { cells[GetCircularIndex(dataStartIndex + count, cells.Count)].SetVisible(false); count++; } } /// <summary> /// Updates the cell. /// </summary> /// <param name="cell">Cell.</param> /// <param name="dataIndex">Data index.</param> /// <param name="forceUpdateContents">If set to <c>true</c> force update contents.</param> void UpdateCell(FancyScrollViewCell<TData, TContext> cell, int dataIndex, bool forceUpdateContents = false) { if (loop) { dataIndex = GetCircularIndex(dataIndex, cellData.Count); } else if (dataIndex < 0 || dataIndex > cellData.Count - 1) { // セルに対応するデータが存在しなければセルを表示しない cell.SetVisible(false); return; } if (forceUpdateContents || cell.DataIndex != dataIndex || !cell.IsVisible) { cell.DataIndex = dataIndex; cell.SetVisible(true); cell.UpdateContent(cellData[dataIndex]); } } /// <summary> /// Creates the cell. /// </summary> /// <returns>The cell.</returns> FancyScrollViewCell<TData, TContext> CreateCell() { var cellObject = Instantiate(cellBase, cellContainer); var cell = cellObject.GetComponent<FancyScrollViewCell<TData, TContext>>(); cell.SetContext(Context); cell.SetVisible(false); cell.DataIndex = -1; return cell; } /// <summary> /// Gets the circular index. /// </summary> /// <returns>The circular index.</returns> /// <param name="index">Index.</param> /// <param name="maxSize">Max size.</param> int GetCircularIndex(int index, int maxSize) { return index < 0 ? maxSize - 1 + (index + 1) % maxSize : index % maxSize; } #if UNITY_EDITOR bool cachedLoop; float cachedCellInterval, cachedCellOffset; void LateUpdate() { if (cachedLoop != loop || cachedCellOffset != cellOffset || cachedCellInterval != cellInterval) { cachedLoop = loop; cachedCellOffset = cellOffset; cachedCellInterval = cellInterval; UpdatePosition(currentPosition); } } #endif } public sealed class FancyScrollViewNullContext { } public abstract class FancyScrollView<TData> : FancyScrollView<TData, FancyScrollViewNullContext> { } }