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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:03:11