首页 > 解决方案 > 使用 ZStack 将动画视图与另一个视图组合

问题描述

我正在开发一个 WatchOS 应用程序,该应用程序需要在等待任务完成时在另一个视图上显示动画。

我的方法如下(ConnectionView):

struct ConnectionView: View{
    @EnvironmentObject var isConnected : Bool

    var body: some View {
       return VStack(alignment: .trailing){
          ZStack{
             ScrollView{
               .....
             } 

             if(!isConnected){
                 ConnectionLoadingView()
             }

          }
       }
    }
}

对于 ConnectionLoadingView:

struct ConnectionLoadView: View {
    @State var isSpinning = false
    @EnvironmentObject var isConnected : Bool

    var body: some View {

    var animation : Animation

    ///This is needed in order to make the animation stop when isConnected is true
    if(!isConnected){
        animation = Animation.linear(duration: 4.0)
    }else{
        animation = Animation.linear(duration: 0)
    }

    return Image(systemName: "arrowtriangle.left.fill")
        .resizable()
        .frame(width: 100, height: 100)
        .rotationEffect(.degrees(isSpinning ? 360 : 0))
        .animation(animation)
        .foregroundColor(.green)
        .onAppear(){
            self.isSpinning = true
        }
        .onDisappear(){
            self.isSpinning = false
        }
    }
}

真正的问题包括两部分:

  1. 在应用程序启动后显示的第一个 ConnectionView 上,ConnectionLoadView 正确显示。在随后的运行中,ConnectionLoadView 有一个奇怪的“淡入”效果,它会在整个动画中改变它的不透明度(如果我将视图的不透明度设置为 1、0 或介于两者之间的任何值都无关紧要)。

  2. 如果我在 ConnectionLoadView 中没有以下代码片段:

    if(!isConnected){
        animation = Animation.linear(duration: 4.0)
    }else{
        animation = Animation.linear(duration: 0)
    }
    

如果没有这个,ConnectionView 将继续播放动画,但将它从 ZStack 的前景移到 ScrollView 后面的背景,什么时候它应该立即消失?如果没有此代码片段,动画只会在动画在任务完成之前停止时才会消失。

当我明确指出仅当且仅当在 ConnectionView 中时才应显示 ConnectionLoadView 时,是否有任何理由将 ConnectionLoadView 推送到 ZStack 的背景而不是完全从视图中删除!isConnected

我也不太明白为什么初始 ConnectionView 的动画行为与后续关于不透明度行为的动画行为之间存在差异。不透明度是线性动画的一部分吗?

谢谢!

标签: iosanimationswiftuiwatchos

解决方案


您正在接近动画错误。您不应该为此使用隐式动画。显式动画更适合。

隐式动画是您使用 .animation() 应用的动画。这会影响视图上更改的任何可动画参数。

显式动画是您触发的动画withAnimation { ... }。只有受闭包内修改的变量影响的参数才会设置动画。其余的不会。

新代码如下所示:

import SwiftUI

class Model: ObservableObject {
    @Published var isConnected = false
}

struct Progress: View{
    @EnvironmentObject var model: Model

    var body: some View {
       return VStack(alignment: .trailing){
          ZStack{
             ScrollView{
                ForEach(0..<3) { idx in
                    Text("Some line of text in row # \(idx)")
                }

                Button("connect") {
                    self.model.isConnected = true
                }
                 Button("disconect") {
                     self.model.isConnected = false
                 }
             }

            if !self.model.isConnected {
                 ConnectionLoadView()
             }

          }
       }
    }
}

struct ConnectionLoadView: View {
    @State var isSpinning = false
    @EnvironmentObject var model: Model

    var body: some View {

    return Image(systemName: "arrowtriangle.left.fill")
        .resizable()
        .frame(width: 100, height: 100)
        .rotationEffect(.degrees(isSpinning ? 360 : 0))
        .foregroundColor(.green)
        .onAppear(){
            withAnimation(Animation.linear(duration: 4.0).repeatForever(autoreverses: false)) {
                self.isSpinning = true
            }
        }
    }
}

推荐阅读