0

我有一个进度视图,其进度/宽度可以设置动画。该动画可自行运行。但是,在某些情况下,父视图会更改其布局,并在进度条上方添加一个新视图。然后,进度视图向下移动,为新视图腾出空间。这也是动画的。在 iOS 13 中同时出现两种动画时,进度视图会向下移动,并且蓝色进度条的宽度会同时进行动画处理。在 iOS 14 中,进度视图向下移动,但蓝色进度条也使用其动画来动画位置变化。在 iOS 13 中,它仅用于为宽度设置动画。这使动画看起来好像进度条飞入了进度视图,并且是错误的并且看起来很奇怪。

我使用以下代码复制了我的应用程序中的代码。此代码还用于录制视频。

struct MainView: View {
    @State var toggle = false
    @State var trim: CGFloat = 0.2

    var body: some View {
        VStack {
            Rectangle().frame(minHeight: 0, maxHeight: self.toggle ? 200 : 0)

            ProgressBarView(trim: self.$trim)

            Button(
                action: {
                    withAnimation {
                        self.toggle.toggle()
                        self.trim = self.toggle ? 0.8 : 0.2
                    }
                },
                label: {
                    Text("Toggle")
                })
        }
    }
}

struct ProgressBarView: View {
    @State var grow: Bool = false
    @Binding var trim: CGFloat

    var body: some View {
        ZStack(alignment: .leading) {
            GeometryReader { geo in
                Rectangle()
                    .opacity(0.1)
                    .zIndex(0)

                Rectangle()
                    .frame(
                        minWidth: 0,
                        maxWidth: self.grow
                            ? geo.frame(in: .global).width * self.trim
                            : 0
                    )
                    .animation(Animation.easeOut(duration: 1.2).delay(0.5))
                    .foregroundColor(Color(UIColor.systemBlue))
            }
        }
        .cornerRadius(10)
        .frame(height: 20)
        .onAppear(perform: {
            self.grow = true
        })
    }
}

更新

这是仍在运行 iOS 13.7 的 iPad 上使用相同代码的原始动画。我只是消除了延迟并增加了时间以使不同的动画更加明显。

更新 2

回答问题时有些困惑,所以我复制了一些框架并将它们放入屏幕截图中。我希望这有助于理解我的问题。左侧是 iOS 14 上的动画,其中包含不需要的行为。如您所见,某些图像中没有出现蓝色进度条。在其他图像中,它只是部分可见。右侧是 iOS 13 动画。图像上的框架(以及所有其他框架)显示完全可见的蓝色进度条。它始终完全可见并位于背景之上。

动画帧比较

我不明白 iOS 14(或者可能是 Swift 5.3 或其他)中的哪些变化导致动画不同,我找不到解决这个问题的方法。

4

1 回答 1

3

进度条视图

嘿!我对结构做了一些改动ProgressBarView。让我们先来看看不太重要的变化。

  • 删除GeometryReader并替换为screen.width. 如果您使用容器内的栏来获取容器的宽度,请使用GeometryReader.

  • 将矩形更改为胶囊。

给重要的

  • 我将栏的动画修饰符定位到触发进度条变化的布尔变量,即grow
struct ProgressBarView: View {
    @State var grow: Bool = false
    @Binding var trim: CGFloat
    
    let screen = UIScreen.main.bounds

    var body: some View {
        VStack {
            ZStack(alignment: .leading) {
                Capsule()
                    .foregroundColor(Color.black.opacity(0.1))
                
                Capsule()
                    .frame(width: self.grow ? screen.width * trim : 0)
                    .foregroundColor(.blue)
                    .animation(Animation.easeOut(duration: 1).delay(0.5), value: self.grow)
            }
            .frame(width: screen.width, height: 20)
            .onAppear {
                self.grow = true
            }
        }
    }
}

更新 1.1

进度条视图

  • ZStack我为 bar 的初始增加从 0 到 0.2添加了一个动画
  • DispatchQueue将延迟更改为 0.18 秒。

延迟变化的原因是,由于矩形的扩展正在改变条在y方向上的位置,持续时间为1.2秒的动画效果被应用于我们没有的位置变化想。因此 0.18 的延迟在矩形扩展后一点点开始条动画。这有点小技巧,但可以完成工作。如果我找到更好的解决方案,我将使用“更新 2.0”更新此答案。如果您找到更好的解决方案,请告诉我。

代码

struct MainView: View {
    @State var toggle = false
    @State var trim: CGFloat = 0.2

    var body: some View {
        VStack {
            Rectangle().frame(minHeight: 0, maxHeight: self.toggle ? 200 : 0)

            ProgressBarView(trim: self.$trim)

            Button(
                action: {
                    withAnimation {
                        self.toggle.toggle()
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.18) {
                            self.trim = self.toggle ? 0.8 : 0.2
                        }
                    }
                },
                label: {
                    Text("Toggle")
                })
        }
    }
}

struct ProgressBarView: View {
    @State var grow: Bool = false
    @Binding var trim: CGFloat
    
    let screen = UIScreen.main.bounds

    var body: some View {
        ZStack(alignment: .leading) {
            Capsule()
                .opacity(0.1)
            
            Capsule()
                .frame(width: self.grow ? screen.width * trim : 0)
                .foregroundColor(.blue)
                .animation(Animation.easeOut(duration: 1.2), value: self.trim)
        }
        .frame(width: screen.width, height: 20)
        .onAppear {
            self.grow = true
        }
        .animation(.easeOut(duration: 0.5), value: grow)
    }
}

我希望这可以帮助你。

于 2020-09-23T13:29:21.130 回答