YUAN
Unity UI 自适应刷新问题.txt
Date: 2026/5/18Tags: UGUIUnity

问题

在前段时间的BooomJam开发和在公司的项目中都遇到了一样的问题。TextMeshPro + ContentSizeFitter + VerticalLayoutGroup 是一套非常常见的组合,用来实现文本内容驱动布局的自适应大小。理想情况下,当你修改文本内容后,面板应该立刻根据新文本的尺寸重新计算布局。但事实上我我遇到了这样的一个问题——面板从隐藏状态切换为显示状态并写入新文案时,首次打开总会看到尺寸或宽度未能立刻适配,只有再次打开才正确的自适应

问题复现

我制作了一个配方描述面板,通过配置表为文本组件进行赋值,结构如下:

RecipeDescPanel (VerticalLayoutGroup + ContentSizeFitter)
└── Text (TextMeshProUGUI + ContentSizeFitter)

当调用 ShowRecipeDesc(string desc) 设置文案并激活面板时:

panel.SetActive(true);
text.text = desc;

我所期望面板宽度立刻根据文本长度自适应。但实际上,第一帧往往显示的是旧尺寸,直到下一次渲染才纠正过来。

原因

Unity 的 UI 布局系统是分帧计算的,依赖 Canvas 的重建管线。具体流程如下:

  1. TextMeshPro 网格生成:TMP 的文本网格生成并非完全同步。当文本内容改变时,TMP 会先标记为 dirty,在后续的网格重建阶段才会真正生成顶点数据。
  2. ContentSizeFitter 计算ContentSizeFitter 依赖子元素的 preferredWidth / preferredHeight。如果 TMP 的网格尚未重建,这些值可能是旧数据。
  3. LayoutGroup 重排VerticalLayoutGroup 同样依赖子元素的尺寸信息,而子元素尺寸又依赖 TMP 的文本布局。
  4. Canvas 重建时机:所有这些刷新操作都在 Canvas.willRenderCanvases 事件中统一处理,这意味着设置文本后至少要等到下一帧的渲染阶段,布局才算真正完成。

更复杂的是,当面板初始处于 inactive 状态时,其下的布局组件甚至不会参与 Canvas 的重建循环,激活后需要额外的帧来"启动"。

解决方案:手动刷新管线

要解决这个问题,不能被动等待 Unity 的自动重建,而是需要主动触发完整的布局刷新链路。以

设置文本后,立刻从底层到上层手动驱动重建:

text.text = desc;

// 1. 强制 TMP 立刻生成网格
text.ForceMeshUpdate();

// 2. 从内到外强制重建布局
LayoutRebuilder.ForceRebuildLayoutImmediate(text.rectTransform);
LayoutRebuilder.ForceRebuildLayoutImmediate(panelRectTransform);

// 3. 强制 Canvas 立即更新
Canvas.ForceUpdateCanvases();

这里的关键是调用顺序:

  • ForceMeshUpdate() 确保 TMP 的文本网格和 preferredWidth 是最新的。
  • ForceRebuildLayoutImmediate 从内向外(Text → Panel)递归计算布局。
  • Canvas.ForceUpdateCanvases() 将整个 Canvas 的重建提前到当前帧。
Published: 2026/5/18