代码之家  ›  专栏  ›  技术社区  ›  Kyle Delaney

在内容的矩形变换被内容大小调整器更新后,如何使Unity Scroll Rect滚动到底部?

  •  10
  • Kyle Delaney  · 技术社区  · 8 年前

    我有一个垂直滚动视图,我想动态添加内容。为了做到这一点,我在内容游戏对象上附加了一个内容大小过滤器组件和一个垂直布局组组件,这样每当我将新游戏对象实例化为其子对象时,其Rect变换就会自动增长。如果滚动条已经在底部,我想在底部添加新对象后,将滚动条保持在底部。所以我是这样做的:

        if ( scrollRect.verticalNormalizedPosition == 0 )
        {
            isAtBottom = true ;
        }
    
        ScrollViewItem item = Instantiate( scrollViewItem, scrollRect.content ) ;
    
        if ( isAtBottom )
        {
            scrollRect.verticalNormalizedPosition = 0 ;
        }
    

    但是,这不起作用,因为新实例化的滚动视图项在我设置时没有增加Rect变换的大小 verticalNormalizedPosition 归零。因此,当Rect变换最终更新时,滚动到底部为时已晚。

    举个例子,假设我的内容有400像素高,滚动条一直在底部。现在我添加了一个100像素高的对象。然后我将滚动条发送到底部,但它仍然认为内容有400像素高。然后内容大小被更新为500像素,但滚动条下降了400像素,因此只下降了80%,而不是100%。

    有两种可能的方法来解决这个问题。我想要一种方法来强制内容大小安装人员立即更新,或者作为一个事件来响应内容大小安装人员的更新。

    通过研究和实验,我几乎成功地实现了第一个选择,将这些行按如下顺序排列:

    Canvas.ForceUpdateCanvases();
    scrollRect.content.GetComponent<VerticalLayoutGroup>().CalculateLayoutInputVertical() ;
    scrollRect.content.GetComponent<ContentSizeFitter>().SetLayoutVertical() ;
    scrollRect.verticalNormalizedPosition = 0 ;
    

    然而,它并没有完全滚动到底部。它总是在20像素左右。所以我想知道是否还有一些布局操作是我不强迫的。也许是填充物什么的。

    4 回复  |  直到 8 年前
        1
  •  15
  •   Kyle Delaney    8 年前

    好的,我想我已经明白了。在大多数情况下, Canvas.ForceUpdateCanvases(); 这是设置前需要做的全部工作 verticalNormalizedPosition 归零。但在我的例子中,我添加到内容本身的项目也有一个垂直布局组组件和一个内容大小调整组件。所以我必须按以下顺序执行这些步骤:

    Canvas.ForceUpdateCanvases();
    
    item.GetComponent<VerticalLayoutGroup>().CalculateLayoutInputVertical() ;
    item.GetComponent<ContentSizeFitter>().SetLayoutVertical() ;
    
    scrollRect.content.GetComponent<VerticalLayoutGroup>().CalculateLayoutInputVertical() ;
    scrollRect.content.GetComponent<ContentSizeFitter>().SetLayoutVertical() ;
    
    scrollRect.verticalNormalizedPosition = 0 ;
    

    遗憾的是,关于这些方法的文档太少了。

        2
  •  6
  •   Wappenull    6 年前

    正确的方法 没有 帆布ForceUpdateCases和疯狂迭代。 Unity 2018.3.12确认工作

    // Assumes
    ScrollRect m_ScrollRect;
    

    在你更新ScrollRect内容并想备份滚动条位置的地方

    float backup = m_ScrollRect.verticalNormalizedPosition;
    
    /* Content changed here */
    
    StartCoroutine( ApplyScrollPosition( m_ScrollRect, backup ) );
    

    为了在没有抖动的情况下应用新的滚动位置,需要在帧的末尾,我们使用协程等待该时间,然后使用 LayoutRebuilder.ForceRebuildLayoutImmediate 仅在该部分上触发布局重建。

    IEnumerator ApplyScrollPosition( ScrollRect sr, float verticalPos )
    {
        yield return new WaitForEndOfFrame( );
        sr.verticalNormalizedPosition = verticalPos;
        LayoutRebuilder.ForceRebuildLayoutImmediate( (RectTransform)sr.transform );
    }
    

    贷方:

        3
  •  4
  •   order    4 年前

    注册就是为了回答这个问题;我找到了一个更快的方法:

    只需设置锚点&内容对象的轴(也可以有一个内容过滤器组件),它将从底部开始,您将向上滚动。

    我的设置是:

    1. “content”对象具有垂直布局组件
    2. “content”对象具有content fitter组件
    3. “content”对象是锚点,使用锚点设置为“bottom stretch”&轴集也是如此

    希望这能帮助那些不想做自定义事情并且无论如何都需要代码的人,干杯

        4
  •  1
  •   exodrifter    7 年前

    这里有一个解决方案,不管有多少嵌套 LayoutGroup s和 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;