SwiftUI饱和度动画异常求助:代码运行结果不符合预期
SwiftUI 渐变矩形动画异常修复
问题现象
制作带颜色循环、饱和度、亮度动画的嵌套矩形时,渐变效果在动画过程中表现异常,颜色循环、饱和度等参数的动画不符合预期。
核心错误点
- AnimatableData 赋值错误:在
animatableData的setter中,错误地将amount和steps都赋值为newValue.first.first,导致amount无法正确跟随动画更新,渐变颜色计算完全错误。 - 未包含所有动画属性:
brightness1和brightness2作为影响渐变颜色的参数,未加入animatableData,导致这些值变化时无法产生平滑动画。 - 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
相关产品推荐
相关产品推荐

