为什么 .animation(_:,value:) 会影响 SwifftUI 中其他属性的变化?

问题描述 投票:0回答:1

我拥有三处房产。两种在变化时具有不同的动画,一种没有动画。

struct Stick: View {
    
    var color: Color
    
    var body: some View {
        GeometryReader { global in
            Path { path in
                path.move(to: CGPoint(x: global.size.width/2, y: 0))
                path.addLine(to: CGPoint(x:global.size.width/2,y: global.size.height))
            }
            .stroke(color,lineWidth: 200)
        }
    }
}

struct SuperStick: View {
    
    @Binding var progress: Float
    @State var offset: CGFloat = 0
    @State var color: Color = .blue.opacity(0.5)
    
    var body: some View {
        GeometryReader { global in
            ZStack(alignment: .bottom) {
                Stick(color: color)
                    .onAppear {
                        if offset >= 1 {
                            offset = 0
                        }
                        offset += 1
                        
                        color = .green.opacity(0.5)
                    }
            }
            .frame(width: global.size.width, height: global.size.height)
            // Animation 1
//            .animation(.linear(duration: 0.1).repeatForever(autoreverses: true), value: color)
            // Animation 2
            .position(x: global.size.width * (0.3+0.4*offset),y: global.size.height/2)
            .animation(.linear(duration: 1).repeatForever(autoreverses: false), value: offset)
            // Animation 3
            .offset(y:CGFloat(1-progress)*global.size.height)
            .animation(.easeInOut(duration: 0.4), value: progress)
            
        }
    }
}

struct TestView: View {
    
    @State var progress: Float = 0.6
    
    var body: some View {
        ZStack{
            SuperStick(progress: $progress)
                .edgesIgnoringSafeArea(.all)
            VStack {
                Stepper("progress: \(String(format: "%.1f", progress))",value: $progress, in: 0.0...1.0, step: 0.2)
                    .padding(.horizontal,40)
            }
        }
    }
}

#Preview {
    TestView()
}

我们可以看到Animation1被注释了,所以应该只有

offset
progress
的动画。但是,我们可以看到
color
也获得了动画效果,其速度与
offset
相同。

然后我尝试为

color
添加动画,希望
color
有自己的动画(只需取消注释
Animation1
下面的行),然而,这一次,
offset
具有与
color
相同的动画它自己的
Animation2

然后最奇怪的事情发生了。

如果你评论

Animation1
但保留
Animation2
,然后尝试更改
progress
,你会发现虽然我们只有
Animation2
Animation3
,而
color
只是受到
offset的某种影响
拥有
Animation2
,

当您更改

progress
时,
Animation2
上的
offset
会停止(被
Animation3
打断),但
color
会不断变化...

为什么这一切会发生?

ios swift swiftui core-animation
1个回答
0
投票

您遇到这些问题是因为,例如,当您使用步进器更改进度时,视图会重新渲染,但由于视图已经显示并且动画已在 onAppear 中初始化,因此它们没有得到正确的重新初始化。我正在专门谈论偏移动画。另外,动画修改器文档是这样说的:

当指定值时,将给定动画应用于此视图 变化。

所以,当你使用这样的东西时,会发生什么:

.animation(.linear(duration: 1).delay(0.1).repeatForever(autoreverses: true), value: color)
是当颜色改变时,它会动画化整个视图,而不仅仅是颜色值。要仅对颜色值进行动画处理,您应该使用 withAnimation 函数,该函数可以对使用 color 属性的代码的任何部分进行动画处理。

我已经修改了你的代码,如下所示:

struct SuperStick: View { @Binding var progress: Float @State var offset: CGFloat = 0 @State var color: Color = .blue.opacity(0.5) var body: some View { let _ = print("Rendered") GeometryReader { global in ZStack(alignment: .bottom) { Stick(color: color) .onAppear { /// Called for the first time. The animation now animates from 0 to 1. offset = 1 withAnimation(.linear(duration: 1).delay(0.1).repeatForever(autoreverses: true)) { color = .green.opacity(0.5) } } } .frame(width: global.size.width, height: global.size.height) // Animation 1 //.animation(.linear(duration: 1).delay(0.1).repeatForever(autoreverses: true), value: color) // Animation 2 .position(x: global.size.width * (0.3+0.4*offset),y: global.size.height/2) .animation(.linear(duration: 5).repeatForever(autoreverses: false), value: offset) // Animation 3 .offset(y:CGFloat(1-progress)*global.size.height) .animation(.easeInOut(duration: 0.4), value: progress) .onChange(of: progress) { oldValue, newValue in withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) { /// Called when progress changes. This resets the animation and make it start again. if offset == 1 { offset = 0 } else { offset = 1 } } } } } }
我使用了 iOS 17 的 onChange 修饰符,但你也可以使用旧的,你只需要在闭包中传递一个参数而不是两个。
在 onChange 中,我重新初始化偏移动画,以便当基于进度值更改发生重新渲染时,动画也会刷新。
我希望这个解释是清楚的,不要犯错误。
让我知道您的想法以及此代码是否适合您!

© www.soinside.com 2019 - 2024. All rights reserved.