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

SwiftUI饱和度动画异常求助:代码运行结果不符合预期

SwiftUI 渐变矩形动画异常修复

问题现象

制作带颜色循环、饱和度、亮度动画的嵌套矩形时,渐变效果在动画过程中表现异常,颜色循环、饱和度等参数的动画不符合预期。

核心错误点

  1. AnimatableData 赋值错误:在animatableData的setter中,错误地将amount和steps都赋值为newValue.first.first,导致amount无法正确跟随动画更新,渐变颜色计算完全错误。
  2. 未包含所有动画属性:brightness1和brightness2作为影响渐变颜色的参数,未加入animatableData,导致这些值变化时无法产生平滑动画。
  3. Tap手势逻辑冗余:onTapGesture中重复两次切换saturation值,导致实际未发生变化,无法触发对应动画。

修复方案

1. 修正AnimatableData的映射

扩展AnimatableData以包含所有需要动画的属性,并在setter中正确赋值每个参数:

var animatableData: AnimatablePair<AnimatablePair<AnimatablePair<Double, Double>, Double>, AnimatablePair<Double, Double>> {
    get {
        AnimatablePair(
            AnimatablePair(
                AnimatablePair(steps, amount),
                saturation
            ),
            AnimatablePair(brightness1, brightness2)
        )
    }
    set {
        steps = newValue.first.first.first
        amount = newValue.first.first.second
        saturation = newValue.first.second
        brightness1 = newValue.second.first
        brightness2 = newValue.second.second
    }
}

2. 修复Tap手势逻辑

移除重复的saturation切换代码,确保参数能正确触发动画:

.onTapGesture {
    withAnimation() {
        saturation = saturation == 1 ? 0 : 1
        colorCycle = colorCycle == 1 ? 0 : 1
    }
}

3. 优化ForEach的稳定性(可选)

由于steps是Double类型,动画过程中Int(steps)会频繁变化导致视图数量突变,可使用Int(steps.rounded())减少视图数量的突变,让动画更平滑。

完整修复代码

import SwiftUI

struct ColorCyclingCircle: View, Animatable {
    var amount = 0.0
    var steps = 100.0
    
    var saturation = 1.0
    var brightness1 = 1.0
    var brightness2 = 0.5
    
    // 修正AnimatableData,包含所有需要动画的属性
    var animatableData: AnimatablePair<AnimatablePair<AnimatablePair<Double, Double>, Double>, AnimatablePair<Double, Double>> {
        get {
            AnimatablePair(
                AnimatablePair(
                    AnimatablePair(steps, amount),
                    saturation
                ),
                AnimatablePair(brightness1, brightness2)
            )
        }
        set {
            steps = newValue.first.first.first
            amount = newValue.first.first.second
            saturation = newValue.first.second
            brightness1 = newValue.second.first
            brightness2 = newValue.second.second
        }
    }
    
    var body: some View {
        ZStack {
            // 使用rounded减少steps动画时的视图数量突变
            ForEach(0..<Int(steps.rounded()), id: \.self) { value in
                Rectangle()
                    .inset(by: Double(value))
                    .strokeBorder(
                        LinearGradient(
                            gradient: Gradient(colors: [
                                color(for: value, brightness: brightness1),
                                color(for: value, brightness: brightness2)
                            ]),
                            startPoint: .top,
                            endPoint: .bottom
                        ),
                        lineWidth: 2
                    )
            }
        }
        .drawingGroup()
    }

    func color(for value: Int, brightness: Double) -> Color {
        var targetHue = Double(value) / Double(steps) + amount

        if targetHue > 1 {
            targetHue -= 1
        }

        return Color(hue: targetHue, saturation: saturation, brightness: brightness)
    }
}

struct ContentView: View {
    @State private var colorCycle = 0.0
    @State private var steps = 100.0
    @State private var saturation = 1.0
    @State private var brightness1 = 1.0
    @State private var brightness2 = 0.5

    var body: some View {
        VStack {
            ColorCyclingCircle(amount: colorCycle, steps: steps, saturation: saturation, brightness1: brightness1, brightness2: brightness2)
                .frame(width: 300, height: 300)
                .onTapGesture {
                    withAnimation() {
                        saturation = saturation == 1 ? 0 : 1
                        colorCycle = colorCycle == 1 ? 0 : 1
                    }
                }

            Spacer()
                .frame(height: 100)
            
            Text("Color")
            Slider(value: $colorCycle)
            Text("Saturation")
            Slider(value: $saturation)
            Text("Brightness")
            Slider(value: $brightness1)
            Slider(value: $brightness2)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:15:33