动画不适用于 SwiftUI 视图状态更改

Posted

技术标签:

【中文标题】动画不适用于 SwiftUI 视图状态更改【英文标题】:Animation not applied to SwiftUI view state change 【发布时间】:2020-02-25 22:04:30 【问题描述】:

我正在尝试对应用于 SwiftUI 文本字段的叠加层的更改进行动画处理。叠加层的状态由状态变量(简单的布尔值)控制,并且大多数参考建议将 .animation() 添加到 Toggle 控件中此变量的绑定中应该可以解决问题。但是,它似乎没有效果。我尝试将相同的修改器添加到用于叠加层的视图中,并将叠加层的组件视图包装在动画块中 - 再一次,没有乐趣。

有趣的是,我在画布或模拟器中运行代码时发现了一个错误,这可能很重要:

2020-02-25 22:00:19.360009+0000 Sandbox[9392:671490] invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.

代码如下(它的原型制作有点粗糙和准备好了)。谁能告诉我我错过了什么?

import SwiftUI

struct ContentView: View 
    @State private var isValid = true

    var body: some View 
        VStack 
            Form 
                TextField("Placeholder", text: .constant(""))
                    .padding(6)
                    .background(ErrorView(isValid: $isValid)
                    .overlay(RoundedRectangle(cornerRadius: 4)
                            .stroke(Color(.systemGray3), lineWidth: 1))
                    .padding()

                Toggle(isOn: $isValid.animation(.easeInOut), label:  Text("Toggle Valid") )
            
        
    


struct ErrorView: View 
    @Binding var isValid: Bool

    var body: some View 
        if isValid 
            return AnyView(EmptyView())
         else 
            return AnyView(
                ZStack 
                    HStack 
                        Spacer()
                        Image(systemName: "exclamationmark.triangle.fill")
                            .foregroundColor(Color.red)
                            .padding(.trailing, 8)
                    

                    HStack 
                        Spacer()
                        Text("error message")
                            .font(.caption)
                            .foregroundColor(Color.red)
                            .offset(x: 0, y: -28)
                    
                
            )
        
    

【问题讨论】:

我知道你说过你这样做了,但我的第一步真的是在所有可能相关的地方坚持.animation() 电话,看看是否有什么不同。 我确实尝试过使用 .animation() “喷洒和祈祷”,但我怀疑问题可能是我正在更改视图层次结构,也许过渡动画会更合适?当我有时间时,我会再深入研究一下 【参考方案1】:

这是一种可能的方法,并且稍微简化了一点(经过测试并适用于 Xcode 11.2 / ios 13.2)

struct ContentView: View 
    @State private var isValid = true

    var body: some View 
        VStack 
            Form 
                TextField("Placeholder", text: .constant(""))
                    .padding(6)
                    .background(ErrorView().opacity(isValid ? 0.0 : 1.0))
                    .overlay(RoundedRectangle(cornerRadius: 4)
                            .stroke(Color(.systemGray3), lineWidth: 1))
                    .padding()

                Toggle(isOn: $isValid.animation(), label:  Text("Toggle Valid") )
            
        
    


struct ErrorView: View 

    var body: some View 
        ZStack 
            HStack 
                Spacer()
                Image(systemName: "exclamationmark.triangle.fill")
                    .foregroundColor(Color.red)
                    .padding(.trailing, 8)
            

            HStack 
                Spacer()
                Text("error message")
                    .font(.caption)
                    .foregroundColor(Color.red)
                    .offset(x: 0, y: -28)
            
        
    

【讨论】:

谢谢!这就是诀窍,正如你所说,它更简单。换出视图(特别是使用 EmptyView()),经过反思,更有可能导致布局的意外副作用。这可能就是原型抛出 CFRunLoop 错误的原因。

以上是关于动画不适用于 SwiftUI 视图状态更改的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI视图状态更改使用withAnimation()方法无动画效果的解决

如何在 SwiftUI 中禁用子视图上的特定状态更改动画

有啥方法可以判断哪个状态更改导致在 SwiftUI 中重建视图?

SwiftUI 深色模式不适用于工作表

使用 @State 变量不适用于从子视图更改 Body 中的视图状态

在 SwiftUI 中动态更改动画的持续时间