You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI多行Text组件展开收起动画异常,该如何正确实现?

多行Text展开收起动画异常解决方案

问题根因

该表现不是代码逻辑错误,属于SwiftUI多行Text的固有特性:当父容器高度动态变化时,Text会立即重新计算适配当前父容器高度的排版(直接收缩为单行截断状态),而非跟随父容器的裁剪进度做过渡,因此会出现文字先跳变、再跟随高度收起的异常效果。DisclosureGroup的收起动画异常也是同一原因导致。

修复方案

核心思路是固定多行Text的实际渲染高度,禁止其随父容器高度变化重排,仅让父容器执行高度变化+裁剪的动画,即可实现和UIKit中UIStackView一致的过渡效果,修改后的完整代码如下:

struct ContentView: View {
    @State var expanded = false
    // 可根据需求替换为实际的长文本内容
    let bigText = "这是一段用于测试的长文本,足够多的内容会触发多行排版,用来验证展开收起动画的表现是否符合预期。"
    
    var body: some View {
        VStack(spacing: 20) {
            HStack {
                Button(expanded ? "收起" : "展开") {
                    withAnimation(.easeInOut(duration: 0.3)) {
                        self.expanded.toggle()
                    }
                }
            }
            VStack(spacing: 10) {
                Text(bigText)
                    // 关键修饰符:固定垂直方向尺寸,禁止Text随父容器高度变化重排
                    .fixedSize(horizontal: false, vertical: true)
                Text(bigText)
                    .fixedSize(horizontal: false, vertical: true)
            }
            // 改用maxHeight适配内容高度,alignment设为top避免裁剪时内容偏移
            .frame(maxHeight: expanded ? .none : 0, alignment: .top)
            .clipped()
            .background(Color.red)
            Text("这是位于大段文本下方的另一段文本。")
                .font(.system(.headline))
                .foregroundColor(.red)
            Spacer()
        }
    }
}

扩展适配说明

如果展开容器内包含除Text外的其他复杂布局,可直接给整个容器添加fixedSize(horizontal: false, vertical: true)修饰符,所有子元素的高度都会预先固定,不会随父容器的高度动画触发重排。该方案同时适配不同屏幕宽度下的文本自动换行,fixedSize仅会固定当前宽度下的文本渲染高度,宽度变化时会自动重新计算后再固定。


内容的提问来源于stack exchange,提问作者Nuno Gonçalves

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 06:39:01