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

SwiftUI中Text视图动态加载点动画优化:消除抖动与偏移

解决SwiftUI加载文本点动画的抖动、模糊与偏移问题

你的代码出现抖动、模糊和文本偏移的核心原因是每次切换不同长度的字符串时,Text视图会重新计算布局,加上全局动画的影响,导致整体位置和渲染不稳定。以下是两种优化方案:

方案一:拆分元素单独控制动画(推荐)

将"Loading"和三个点拆分为独立的Text组件,通过控制每个点的透明度实现动画,彻底避免布局变化:

struct TextViewTest: View {
    @State private var showDot1 = false
    @State private var showDot2 = false
    @State private var showDot3 = false
    
    var body: some View {
        HStack(spacing: 0) {
            Text("Loading")
            // 每个点单独做透明度动画
            Text(".")
                .opacity(showDot1 ? 1 : 0)
                .animation(.easeInOut(duration: 0.3), value: showDot1)
            Text(".")
                .opacity(showDot2 ? 1 : 0)
                .animation(.easeInOut(duration: 0.3), value: showDot2)
            Text(".")
                .opacity(showDot3 ? 1 : 0)
                .animation(.easeInOut(duration: 0.3), value: showDot3)
        }
        .onAppear {
            startDotAnimation()
        }
    }
    
    private func startDotAnimation() {
        Timer.scheduledTimer(withTimeInterval: 0.6, repeats: true) { _ in
            // 重置所有点状态
            showDot1 = false
            showDot2 = false
            showDot3 = false
            
            // 依次触发点的显示动画
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                showDot1 = true
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
                showDot2 = true
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
                showDot3 = true
            }
        }
    }
}

优势:

  • HStack固定了整体布局结构,"Loading"和三个点的位置不会变化,彻底消除偏移
  • 每个点仅做透明度动画,无布局重绘,避免抖动和模糊
  • 动画逻辑清晰,重复触发不会出现时序混乱

方案二:固定文本布局空间

如果希望保留单个Text组件的写法,可以通过固定宽度或让点字符串长度恒定,避免布局重计算:

struct TextViewTest: View {
    @State private var dotCount = 0
    
    var body: some View {
        Text("Loading\(dots)")
            // 固定文本宽度,确保布局不随点数量变化
            .frame(width: 100, alignment: .leading)
            // 仅对内容变化应用平滑动画,禁用布局动画
            .animation(.easeInOut(duration: 0.3), value: dotCount)
            .onAppear {
                startAnimation()
            }
    }
    
    private var dots: String {
        // 生成对应数量的点,不足3个时补空格维持长度恒定
        String(repeating: ".", count: dotCount)
            .padding(toLength: 3, withPad: " ", startingAt: 0)
    }
    
    private func startAnimation() {
        Timer.scheduledTimer(withTimeInterval: 0.6, repeats: true) { _ in
            dotCount = 0
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                dotCount = 1
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
                dotCount = 2
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
                dotCount = 3
            }
        }
    }
}

优势:

  • 单个Text组件保持代码简洁
  • 通过固定宽度或补空格维持文本长度恒定,避免布局偏移
  • 动画仅作用于文本内容,无多余的布局过渡

内容的提问来源于stack exchange,提问作者Dobry Siabar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:35:18