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

SwiftUI的onAppear中直接调用withAnimation为什么会出现界面异常?

struct MyShimmer: View {
    @State var show = false

    var body: some View {
        ZStack {
            Color.black
            VStack {
                ZStack {
                    Text("Chao")
                        .foregroundColor(Color.white.opacity(0.5))
                        .font(.system(size: 40))
                    Text("Chao")
                        .foregroundColor(Color.white)
                        .font(.system(size: 40))
                        .mask(
                            Color.red.opacity(0.5)
                                .frame(width: 20)
                                .offset(x: self.show ? 180 : -130)
                        )
                }
                .onAppear(){
                    DispatchQueue.main.async {
                        withAnimation(Animation.default.speed(0.15).delay(0).repeatForever(autoreverses: false)){
                            self.show.toggle()
                        }
                    }
                }
            }
        }
    }
}
问题原因说明

这个现象是SwiftUI旧版本中onAppear的调用时机和动画事务的作用范围共同导致的,具体逻辑如下:

  • onAppear的触发时机是视图刚被加入渲染树、还未完成初始布局计算的阶段。这个时候直接在onAppear闭包内执行withAnimation修改状态,当前所有未完成的布局变更(包括整个视图从默认的zero frame到最终屏幕位置的布局过程)都会被纳入当前动画事务,所以你会看到整个视图从左上角缩放弹出的效果,动画意外作用到了整个视图的布局过程,而不是仅作用在你想要的mask偏移上。
  • 加DispatchQueue.main.async之后,修改状态的逻辑会被推迟到主队列的下一个RunLoop执行,此时视图已经完成了初始布局,确定了自己在屏幕上的最终位置和尺寸,后续触发的动画只会响应show状态对应的mask偏移变化,自然就不会出现异常的整体弹出效果。
  • 放在按钮点击事件中不会出问题的原因同理:按钮能够响应点击事件时,视图已经完成了初始布局并稳定渲染在屏幕上,此时触发的动画只会作用于你修改的状态对应的属性变更,不会牵连初始布局过程。

补充说明:这个问题属于SwiftUI 2.0(对应Xcode 12版本)的已知常见问题,后续新版本SwiftUI调整了onAppear的调用时机,已经大幅降低了这类问题的出现概率。如果要兼容旧版本,除了用DispatchQueue.main.async之外,也可以给不需要参与动画的外层视图添加.animation(nil, value: show)修饰符,隔离状态变更对整体布局的动画影响。

内容的提问来源于stack exchange,提问作者Chao Huang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 21:06:02