为什么动画在List中不起作用,但在VStack中却起作用

问题描述 投票:0回答:2

进度条在

VStack
中显示动画,但在
List
中不显示动画。 知道为什么吗?

import SwiftUI

struct NoAmimationDemo: View {
    
    @State var progress: Double = 0.0
    
    var body: some View {
        VStack {
            Button() {
                withAnimation {
                    progress += 20.0
                }
            } label: { Text("Update") }
            
            // ✅ Animating 
            ProgressView("", value: progress, total: 100.0)

            List {
                // ❌ Not animating 
                ProgressView("", value: progress, total: 100.0)
            }
        }
    }
}
swift swiftui
2个回答
1
投票

我相信这是一个错误,并且预期的行为是它应该按原样工作。毕竟,如果您将进度视图动画更改为其他内容,例如:

Text("Rotation")
    .rotationEffect(Angle(degrees: progress))

然后两个

Text
都可以正确动画。

你也可以做

ProgressView("", value: progress, total: 100.0)
    .rotationEffect(Angle(degrees: progress))

看到进度条旋转有动画效果,但“进度”没有动画效果。讽刺!

也就是说,出于某种原因,你可以通过在非工作进度条上添加

.transaction
修饰符来解决这个问题 - 你甚至不需要在闭包中执行任何操作。

ProgressView("", value: progress, total: 100.0)
    .transaction { _ in }

即使是一些完全不相关的东西,比如

.badge
,也可以修复它:

ProgressView("", value: progress, total: 100.0)
    .badge(0) // the number doesn't seem to matter

似乎有一组特定的修饰符可以修复此行为。我的猜测是,这个集合中的修饰符都在其实现中对

ProgressView
做了一些特定的事情,这就是使进度视图“意识到”当前
Transaction
是动画的原因。

现在我只是在最后加上一个

.transaction { _ in }
,因为除了修复这个问题之外,它没有做任何特别的事情:)


0
投票

正如其他人提到的,

List
不能很好地处理动画。如果你愿意用 ScrollView + VStack 制作自己的解决方案,我有一个解决方案。

问题是您只设置if内容应该出现,而不是how应该出现。

因此,您只需在每次致电

maxHeight
时设置
isExpanded.toggle

struct CustomDisclosureGroup<Label: View, Content: View>: View {
    @State private var isExpanded: Bool = false

    // 1. Add contentHeight

    @State private var contentHeight: CGFloat = .zero

    private let label: Label
    private let content: Content

    init(@ViewBuilder label: () -> Label, @ViewBuilder content: () -> Content) {
        self.label = label()
        self.content = content()
    }

    var body: some View {
        VStack(spacing: 0) {
            Button {
                withAnimation {
                    isExpanded.toggle()

                    // 2. Set the content height property with a delay so you can observe it

                    withAnimation(.easeIn.delay(0.01)) {
                        if isExpanded {
                            contentHeight = .infinity
                        } else {
                            contentHeight = .zero
                        }
                    }
                }


            } label: {
                HStack {
                    label
                    Spacer()
                }
            }

            if isExpanded {

                // 3. Set the height of the content

                content
                    .frame(maxHeight: contentHeight)
            }
        }
    }
}

struct ContentView: View {
    @State var items: [Int] = [0, 1, 2, 3]

    var body: some View {

        // List replaced by ScrollView and VStack

        ScrollView {
            VStack {
                ForEach(items, id: \.self) { item in
                    CustomDisclosureGroup {
                        Text(item.formatted())
                    } content: {
                        Text("Test text")
                        Text("Test text")
                        Text("Test text")
                    }
                }
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.