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

Flutter集成SwiftUI雪花动画:解决导航切换时Timer重复创建问题

解决SwiftUI雪花动画Timer重复运行导致的加速异常

当前实现中,通过UiKitView加载的SnowFallAnimationView每次显示时,onAppear都会触发startSnowFall创建新Timer,导航离开页面时旧Timer未被销毁,返回后多个Timer叠加运行,导致雪花位置被多次更新,出现持续加速的异常效果。以下是两种针对性解决方案:

方案一:页面消失时停止Timer

利用SwiftUI的onDisappear修饰符,在页面离开时停止Timer,彻底终止后台运行的定时器,避免残留Timer继续影响动画状态。

修改后的完整SwiftUI代码:

import SwiftUI

struct Snowflake: Identifiable {
    var id = UUID()
    var x: Double
    var y: Double
    var scale: Double
    var speed: Double
}


struct SnowFallAnimationView: View{
    @State private var snowflakes: [Snowflake] = []
    @State private var timer: Timer?
    
    var body: some View {
        ZStack {
            LinearGradient(
                gradient: Gradient(colors: [.black, .black]), 
                startPoint: .top, 
                endPoint: .bottom
            )
            
            if #available(iOS 15.0, *) {
                Canvas { context, size in
                    for snowflake in snowflakes {
                        context.draw(
                            Text("❄️").font(.system(size: 10 * snowflake.scale)), 
                            at: CGPoint(x: snowflake.x * size.width, y: snowflake.y * size.height)
                        )
                    }
                }
            }
        }
        .onAppear() {
            startSnowFall()
        }
        .onDisappear() {
            stopSnowFall()
        }
    }
    
    func startSnowFall() {
        // 仅在雪花数据为空时初始化
        if snowflakes.isEmpty {
            for _ in 0..<50 {
                snowflakes.append(
                    Snowflake(
                        x: Double.random(in: 0...1),
                        y: Double.random(in: -0.2...0),
                        scale: Double.random(in: 0.5...1.5),
                        speed: Double.random(in: 0.001...0.003)
                    )
                )
            }
        }
        // 创建新Timer前先停止旧Timer
        timer?.invalidate()
        timer = Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) { _ in
            for i in snowflakes.indices {
                snowflakes[i].y += snowflakes[i].speed
                if snowflakes[i].y >= 1.2 {
                    snowflakes[i].y = -0.2
                    snowflakes[i].x = Double.random(in: 0...1)
                }
            }
        }
    }
    
    func stopSnowFall() {
        timer?.invalidate()
        timer = nil
        // 可选:重置雪花位置,下次进入页面重新开始动画
        // snowflakes.removeAll()
    }
}

方案二:复用已存在的Timer,避免重复创建

在startSnowFall中先检查Timer是否处于运行状态,仅在未运行时创建新Timer,同时确保雪花数据只初始化一次,从根源上避免多Timer叠加。

核心修改逻辑:

func startSnowFall() {
    // 初始化雪花数据(仅执行一次)
    if snowflakes.isEmpty {
        for _ in 0..<50 {
            snowflakes.append(
                Snowflake(
                    x: Double.random(in: 0...1),
                    y: Double.random(in: -0.2...0),
                    scale: Double.random(in: 0.5...1.5),
                    speed: Double.random(in: 0.001...0.003)
                )
            )
        }
    }
    // Timer已运行则直接返回,避免重复创建
    guard timer == nil || !timer!.isValid else {
        return
    }
    // 创建新Timer
    timer = Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) { _ in
        for i in snowflakes.indices {
            snowflakes[i].y += snowflakes[i].speed
            if snowflakes[i].y >= 1.2 {
                snowflakes[i].y = -0.2
                snowflakes[i].x = Double.random(in: 0...1)
            }
        }
    }
}

同时保留onDisappear的停止逻辑,确保页面离开时释放资源:

.onDisappear() {
    timer?.invalidate()
    timer = nil
}

补充说明

  • 两种方案结合使用效果最优:离开页面停止Timer避免后台资源浪费,进入时检查Timer状态避免重复创建。
  • Flutter侧代码无需修改,UiKitView的生命周期会与Flutter页面同步,自动触发SwiftUI的onAppear和onDisappear回调。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 09:07:35