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

iOS 17更新后EaseInOut动画无法正常工作问题求助

iOS 17下SwiftUI循环淡入淡出动画停止问题

我写了一个Blink ViewModifier,在iOS 16里能持续平滑地淡入淡出,但升级到iOS 17后,动画只跑几次就停了。

相关代码:

struct Blink: ViewModifier {

@State private var opacity = 0.0

var isActive: Bool

func body(content: Content) -> some View {
    if isActive {
        content
            .opacity(self.opacity)
            .animation(.easeInOut(duration: 0.5)
                .repeatCount(1)
                .repeatForever(autoreverses: true),
                       value: self.opacity)
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    self.opacity = 1
                }
            }.onChange(of: opacity) {
              print(opacity)
            }
    }
    else {
        content
    }
    
}
}

iOS 17下的动画效果:
iOS 17中的动画效果


问题根源

iOS 17对SwiftUI动画的重复逻辑做了调整,原代码里同时用.repeatCount(1)和.repeatForever(autoreverses: true)会造成动画系统冲突。再加上只靠一次opacity值变更触发动画,iOS 17的动画优化机制会判定动画已完成,直接终止循环。

修复方案

方案1:清理重复配置,简化动画触发

删掉多余的.repeatCount(1),让永久重复的动画逻辑正常生效:

struct Blink: ViewModifier {
    @State private var opacity = 0.0
    var isActive: Bool

    func body(content: Content) -> some View {
        Group {
            if isActive {
                content
                    .opacity(opacity)
                    .animation(
                        .easeInOut(duration: 0.5)
                            .repeatForever(autoreverses: true),
                        value: opacity
                    )
                    .onAppear {
                        // 保留原有的延迟触发,按需调整或直接移除延迟
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                            opacity = 1
                        }
                    }
            } else {
                content
            }
        }
    }
}

方案2:用Timer主动控制动画状态(更稳定)

如果方案1还是有问题,用Timer周期性切换opacity值,确保动画持续触发:

struct Blink: ViewModifier {
    @State private var opacity = 0.0
    @State private var timer: Timer?
    var isActive: Bool
    let animationDuration: Double = 0.5

    func body(content: Content) -> some View {
        Group {
            if isActive {
                content
                    .opacity(opacity)
                    .animation(.easeInOut(duration: animationDuration), value: opacity)
                    .onAppear {
                        startBlinkTimer()
                    }
                    .onDisappear {
                        timer?.invalidate()
                    }
                    .onChange(of: isActive) { newValue in
                        if newValue {
                            startBlinkTimer()
                        } else {
                            timer?.invalidate()
                            opacity = 0 // 重置透明度
                        }
                    }
            } else {
                content
            }
        }
    }

    private func startBlinkTimer() {
        timer?.invalidate()
        // 动画往返一次的时间是duration*2
        timer = Timer.scheduledTimer(withTimeInterval: animationDuration * 2, repeats: true) { _ in
            opacity = opacity == 0 ? 1 : 0
        }
        // 初始延迟触发一次,和原逻辑保持一致
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            opacity = 1
        }
    }
}

方案3:用AnimatableModifier自定义动画(精准控制)

如果需要更底层的动画控制,实现AnimatableModifier:

struct Blink: AnimatableModifier {
    var isActive: Bool
    private var progress: Double = 0

    var animatableData: Double {
        get { progress }
        set { progress = newValue }
    }

    func body(content: Content) -> some View {
        content
            .opacity(isActive ? sin(progress * .pi) : 1)
            .onAppear {
                guard isActive else { return }
                withAnimation(.easeInOut(duration: 0.5).repeatForever(autoreverses: false)) {
                    progress = .infinity
                }
            }
            .onChange(of: isActive) { newValue in
                guard newValue else {
                    progress = 0
                    return
                }
                withAnimation(.easeInOut(duration: 0.5).repeatForever(autoreverses: false)) {
                    progress = .infinity
                }
            }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 08:47:06