SwiftUI可展开列表选中文字加粗时出现异常动画问题
解决SwiftUI可展开列表选中时文字动画异常问题
你的问题核心是两层Text视图的布局动画不同步:Overlay的常规Text依赖底层加粗Text的布局,而SwiftUI动画系统在处理透明度切换时,会同时触发布局位置的计算——加粗文字宽度更宽,常规文字窄,导致切换时常规Text的位置动画滞后于加粗文字。
下面是几个直接可行的解决方案:
方案一:单Text+固定宽度(推荐)
不用叠加两层Text,直接动态切换字体粗细,同时提前计算并固定文本的最大宽度(加粗状态的宽度),从根源避免布局跳动:
struct ColorListItemView: View { let title: String @Binding var isExpanded: Bool // 预计算加粗文本的宽度,确保布局稳定 private var fixedTextWidth: CGFloat { let boldFont = UIFont.systemFont(ofSize: 16, weight: .bold) let textSize = (title as NSString).size(withAttributes: [.font: boldFont]) return textSize.width } var body: some View { Text(title) .font(isExpanded ? .system(size: 16, weight: .bold) : .system(size: 16)) .frame(width: fixedTextWidth, alignment: .leading) .animation(.easeInOut(duration: 0.2), value: isExpanded) } }
方案二:ZStack替代Overlay
ZStack让两个Text完全独立布局后重叠,避免Overlay的布局依赖问题,确保动画同步:
struct ColorListItemView: View { let title: String @Binding var isExpanded: Bool var body: some View { ZStack(alignment: .leading) { // 加粗文本 Text(title) .font(.system(size: 16, weight: .bold)) .opacity(isExpanded ? 1 : 0) // 常规文本 Text(title) .font(.system(size: 16)) .opacity(isExpanded ? 0 : 1) } .frame(maxWidth: .infinity, alignment: .leading) .animation(.easeInOut, value: isExpanded) } }
方案三:禁用Overlay视图的位置动画
如果必须保留Overlay写法,可以给上层常规Text禁用位置动画,只保留透明度变化:
struct ColorListItemView: View { let title: String @Binding var isExpanded: Bool var body: some View { Text(title) .font(.system(size: 16, weight: .bold)) .opacity(isExpanded ? 1 : 0) .overlay( Text(title) .font(.system(size: 16)) .opacity(isExpanded ? 0 : 1) .animation(nil, value: isExpanded) // 禁止位置动画,只保留透明度 ) .animation(.easeInOut, value: isExpanded) } }
额外提示
- 优先选方案一,因为它只维护一个视图实例,布局逻辑更简单,动画性能也更好
- 如果需要支持动态字体大小,记得把
fixedTextWidth的计算换成动态字体的逻辑
内容的提问来源于stack exchange,提问作者CalebK
相关产品推荐
相关产品推荐

