SwiftUI位置和框架动画在iOS 14中损坏

时间:2020-09-23 09:13:41

标签: swiftui ios14 swiftui-animation

我有一个进度视图,其进度/宽度可以设置为动画。该动画本身起作用。但是,在某些情况下,父视图会更改其布局,并在进度栏上方添加一个新视图。然后,进度视图下移以为新视图腾出空间。这也是动画。当两种动画都在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
        })
    }
}

更新

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

更新2

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

Animation frame comparison

我不了解iOS 14中的哪些更改(或者可能是Swift 5.3或其他任何更改)导致动画有所不同,并且找不到解决此问题的方法。

1 个答案:

答案 0 :(得分:3)

ProgressBarView

嘿!我对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

ProgressBarView

  • 我在ZStack上添加了动画,以将bar从0初始增加到0.2
  • DispatchQueue的延迟时间更改为0.18秒。

延迟变化的原因是,由于矩形的扩展改变了条形图在y方向上的位置,因此将持续时间为1.2秒的动画效果应用于位置变化,不想要因此,延迟为0.18会在矩形展开后开始一点动画。这有点hack,但是可以完成工作。如果找到更好的解决方案,我将使用“ Update 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)
    }
}

希望对您有帮助。