[英]How do I get a Unity Scroll Rect to scroll to the bottom after the content's Rect Transform is updated by a Content Size Fitter?
我有一個垂直滾動視圖,我想動態地向其中添加內容。 為了做到這一點,我將一個 Content Size Fitter 組件和一個 Vertical Layout Group 組件附加到 Content 游戲對象,這樣每當我將新游戲對象實例化為它的子級時,它的 Rect Transform 將自動增長。 如果滾動條已經在底部,我想在底部添加新對象后將滾動條保持在底部。 所以我這樣做:
if ( scrollRect.verticalNormalizedPosition == 0 )
{
isAtBottom = true ;
}
ScrollViewItem item = Instantiate( scrollViewItem, scrollRect.content ) ;
if ( isAtBottom )
{
scrollRect.verticalNormalizedPosition = 0 ;
}
但是,這不起作用,因為到我將verticalNormalizedPosition
設置為零時,新實例化的滾動視圖項並沒有增加 Rect Transform 的大小。 所以當 Rect Transform 終於更新完時,再滾動到底部就來不及了。
為了說明,假設我的內容高 400 像素,滾動條一直在底部。 現在我向它添加一個 100 像素高的對象。 然后我將滾動條發送到底部,但它仍然認為內容是 400 像素高。 然后內容大小更新為 500 像素,但滾動條向下 400 像素,因此它只是向下的 80% 而不是 100%。
有兩種可能的方法來解決這個問題。 我想要一種強制 Content Size Fitter 立即更新的方法,或者一種響應 Content Size Fitter 作為事件更新的方法。
通過研究和實驗,通過將這些行按以下確切順序排列,我幾乎在第一個選項中取得了成功:
Canvas.ForceUpdateCanvases();
scrollRect.content.GetComponent<VerticalLayoutGroup>().CalculateLayoutInputVertical() ;
scrollRect.content.GetComponent<ContentSizeFitter>().SetLayoutVertical() ;
scrollRect.verticalNormalizedPosition = 0 ;
但是,它並沒有完全滾動到底部。 它總是在大約 20 像素之外。 所以我想知道是否還有一些我不強迫發生的布局操作。 也許這是填充或其他東西。
好吧,我相信我已經想通了。 大多數情況下, Canvas.ForceUpdateCanvases();
這是您在將verticalNormalizedPosition
設置為零之前需要做的所有事情。 但就我而言,我添加到內容本身的項目也有一個 Vertical Layout Group 組件和一個 Content Size Fitter 組件。 所以我必須按以下順序執行這些步驟:
Canvas.ForceUpdateCanvases();
item.GetComponent<VerticalLayoutGroup>().CalculateLayoutInputVertical() ;
item.GetComponent<ContentSizeFitter>().SetLayoutVertical() ;
scrollRect.content.GetComponent<VerticalLayoutGroup>().CalculateLayoutInputVertical() ;
scrollRect.content.GetComponent<ContentSizeFitter>().SetLayoutVertical() ;
scrollRect.verticalNormalizedPosition = 0 ;
有點遺憾的是,圍繞這些方法的文檔太少了。
沒有Canvas.ForceUpdateCanvases 和瘋狂迭代的正確方法。 在 Unity 2018.3.12 中確認工作
// Assumes
ScrollRect m_ScrollRect;
以及您更新 ScrollRect 內容並希望備份滾動條位置的某處
float backup = m_ScrollRect.verticalNormalizedPosition;
/* Content changed here */
StartCoroutine( ApplyScrollPosition( m_ScrollRect, backup ) );
並且要在沒有抖動的情況下應用新的滾動位置,它需要在幀結束時,我們使用 Coroutine 等待該時間,然后使用LayoutRebuilder.ForceRebuildLayoutImmediate僅在該部分觸發布局重建。
IEnumerator ApplyScrollPosition( ScrollRect sr, float verticalPos )
{
yield return new WaitForEndOfFrame( );
sr.verticalNormalizedPosition = verticalPos;
LayoutRebuilder.ForceRebuildLayoutImmediate( (RectTransform)sr.transform );
}
歸功於:
注冊只是為了回答這個問題; 我找到了一個更快的方法:
只需設置內容對象的錨點和樞軸(它也可以有一個內容過濾器組件),它就會從底部開始向上滾動。
我的設置是:
希望這對那些不想做一些自定義並且無論如何都需要代碼的人有所幫助,干杯
這是一個無論有多少嵌套的LayoutGroup
和ContentSizeFitter
都有效的解決方案:
using UnityEngine;
using UnityEngine.UI;
public static class UIX
{
/// <summary>
/// Forces the layout of a UI GameObject and all of it's children to update
/// their positions and sizes.
/// </summary>
/// <param name="xform">
/// The parent transform of the UI GameObject to update the layout of.
/// </param>
public static void UpdateLayout(Transform xform)
{
Canvas.ForceUpdateCanvases();
UpdateLayout_Internal(xform);
}
private static void UpdateLayout_Internal(Transform xform)
{
if (xform == null || xform.Equals(null))
{
return;
}
// Update children first
for (int x = 0; x < xform.childCount; ++x)
{
UpdateLayout_Internal(xform.GetChild(x));
}
// Update any components that might resize UI elements
foreach (var layout in xform.GetComponents<LayoutGroup>())
{
layout.CalculateLayoutInputVertical();
layout.CalculateLayoutInputHorizontal();
}
foreach (var fitter in xform.GetComponents<ContentSizeFitter>())
{
fitter.SetLayoutVertical();
fitter.SetLayoutHorizontal();
}
}
}
像這樣使用它:
UIX.UpdateLayout(canvasTransform); // This canvas contains the scroll rect
scrollRect.verticalNormalizedPosition = 0f;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.