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

SwiftUI动画与scaleEffect异常行为排查及修复咨询

问题原因

每次切换scale值时,SwiftUI会为新的scale变化创建一个新的无限重复动画,但之前的无限动画并未被终止,多个动画叠加后导致UI行为紊乱,出现看似多个脉动效果叠加的异常。

修复方案

核心思路是确保每次切换目标缩放比例时,终止旧的无限动画并启动新的连贯动画,避免叠加。以下是两种可行的实现方式:

方式一:显式控制动画生命周期

通过onAppear和onChange监听目标缩放比例的变化,每次变化时重置动画状态并启动新的无限动画:

import SwiftUI

struct AnimatedView: View {
    let targetScale: CGFloat
    @State private var currentScale: CGFloat = 1.0
    
    var body: some View {
        Circle()
            .frame(width: 100, height: 100)
            .scaleEffect(currentScale)
            .onAppear {
                startPulsing()
            }
            .onChange(of: targetScale) { _ in
                startPulsing()
            }
    }
    
    private func startPulsing() {
        // 先重置缩放比例到初始值,避免动画叠加
        currentScale = 1.0
        // 启动新的无限脉动动画
        withAnimation(Animation.easeInOut.repeatForever(autoreverses: true)) {
            currentScale = targetScale
        }
    }
}

struct TestAnimation: View {
    @State private var large_pulsate: Bool = false
    var body: some View {
        AnimatedView(targetScale: large_pulsate ? 2.0 : 1.1)
            .onTapGesture {
                large_pulsate.toggle()
            }
    }
}

struct TestAnimation_Previews: PreviewProvider {
    static var previews: some View {
        TestAnimation()
    }
}

方式二:使用动画修饰符直接绑定无限动画

另一种更简洁的方式是将无限动画直接应用到视图上,通过动态修改动画的目标值来切换脉动尺寸,确保动画始终只有一个实例:

import SwiftUI

struct AnimatedView: View {
    let maxScale: CGFloat
    @State private var isPulsing = false
    
    var body: some View {
        Circle()
            .frame(width: 100, height: 100)
            .scaleEffect(isPulsing ? maxScale : 1.0)
            .animation(Animation.easeInOut.repeatForever(autoreverses: true), value: isPulsing)
            .onAppear {
                isPulsing = true
            }
            .onChange(of: maxScale) { _ in
                // 切换目标尺寸时,短暂重置动画状态以避免叠加
                isPulsing = false
                DispatchQueue.main.async {
                    isPulsing = true
                }
            }
    }
}

struct TestAnimation: View {
    @State private var large_pulsate: Bool = false
    var body: some View {
        AnimatedView(maxScale: large_pulsate ? 2.0 : 1.1)
            .onTapGesture {
                large_pulsate.toggle()
            }
    }
}

两种方式都能保证每次切换时只有一个无限动画在运行,解决动画叠加导致的紊乱问题,实现点击时在大、小尺寸脉动效果间平滑切换的预期行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 09:51:10