【问题标题】:SwiftUI: Broken explicit animations in NavigationView?SwiftUI:NavigationView 中的显式动画损坏?
【发布时间】:2021-02-10 10:38:37
【问题描述】:

当我在 NavigationView 中放置显式动画时,作为不良副作用,它会为 NavigationView 内容的初始布局设置动画。无限动画尤其糟糕。有没有办法消除这种副作用?

示例:下面的图像应该是全屏蓝色背景上的动画红色加载器。相反,我得到了一个缩放蓝色背景的无限循环:

import SwiftUI

struct EscapingAnimationTest: View {
    var body: some View {
        NavigationView {
            VStack {
                Spacer()
                EscapingAnimationTest_Inner()
                Spacer()
            }
            .backgroundFill(Color.blue)
        }
    }
}

struct EscapingAnimationTest_Inner: View {
    @State var degrees: CGFloat = 0
    
    var body: some View {
        Circle()
            .trim(from: 0.0, to: 0.3)
            .stroke(Color.red, lineWidth: 5)
            .rotationEffect(Angle(degrees: degrees))
            .onAppear() {
                withAnimation(Animation.linear(duration: 1).repeatForever(autoreverses: false)) {
                    degrees = 360
                }
            }
    }
}

struct EscapingAnimationTest_Previews: PreviewProvider {
    static var previews: some View {
        EscapingAnimationTest()
    }
}

【问题讨论】:

    标签: animation swiftui


    【解决方案1】:

    withAnimation 块之外使用DispatchQueue.main.async 对我有用,但这段代码看起来不是很干净。 我找到了另一个(在我看来更干净的)解决方案,它是:

    • 在主体外部创建一个isAnimating 变量
    @State var isAnimating = false 
    

    然后在外部 VStack 的末尾,在 onAppear 中将此变量设置为 true。然后使用 isAnimating 三元运算符调用rotationEffect,然后调用 clal .animation() 。完整代码如下:

        var body: some View {
            VStack {
                // the trick is to use .animation and some helper variables
                Circle()
                   .trim(from: 0.0, to: 0.3)
                   .stroke(Color.red, lineWidth: 5)
                   .rotationEffect(Angle(degrees: isAnimating ? 360 : 0))
                   .animation(Animation.linear(duration:1).repeatForever(autoreverses: false), value: isAnimating)
            } //: VStack
            .onAppear {
                isAnimating = true
            }
        }
    

    这样就不需要使用 DispatchQueue.main.async。

    【讨论】:

      【解决方案2】:

      这里是固定部分。使用 Xcode 12 / iOS 14 测试。

      struct EscapingAnimationTest_Inner: View {
          @State var degrees: CGFloat = 0
          
          var body: some View {
              Circle()
                  .trim(from: 0.0, to: 0.3)
                  .stroke(Color.red, lineWidth: 5)
                  .rotationEffect(Angle(degrees: Double(degrees)))
                  .animation(Animation.linear(duration: 1).repeatForever(autoreverses: false), value: degrees)
                  .onAppear() {
                          DispatchQueue.main.async {
                          degrees = 360
                          }
                  }
          }
      }
      

      更新:同样将使用withAnimation

      .onAppear() {
          DispatchQueue.main.async {
              withAnimation(Animation.linear(duration: 1).repeatForever(autoreverses: false)) {
                 degrees = 360
              }
          }
      
      }
      

      【讨论】:

      • 谢谢。您将其更改为隐式动画。我想保留明确的动画。
      • 它是明确的,因为与价值相关。
      • 我的错,我错过了 .animation() 方法中的最终“值”参数。您的两个解决方案都有效,谢谢!顺便说一句,您知道为什么在我的原始代码中显式动画正在启动 NavigationView 动画吗?从概念上讲,它不应该。这是一个错误吗?
      • 您知道为什么将withAnimation 包裹在DispatchQueue.main.async {} 中可以解决问题吗?
      • 我遇到了同样的问题,包裹在 DispatchQueue.main.async 中帮助了我。但我真的很想知道它为什么以及如何工作:)
      猜你喜欢
      • 2020-04-09
      • 2021-06-12
      • 2013-09-21
      • 2014-07-18
      • 2018-02-28
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多