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
相关产品推荐
相关产品推荐

