[英]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.