在 SwiftUI 中,有没有办法扩展和收缩视图以在视图的扩展版本中显示隐藏的文本和按钮?

vid*_*dvi 2 xcode animation ios swift swiftui

我正在制作一个简单的任务应用程序,并使用 ForEach 使用模型中的任务信息填充任务行。我需要一种方法来动画我的任务视图以打开并显示一些描述文本和两个按钮。我想在点击时从 A 变为 B,然后再点击返回:

设计形象

我尝试过几件事。我在测试项目中成功获得了一个概念验证矩形动画,但存在问题。矩形从中心点收缩和增大,而不是仅从底部收缩和增大。当我将文本放入其中时,文本不会被隐藏,而且看起来非常糟糕。

struct ContentView: View {
@State var animate = false
var animation: Animation = .spring()

var body: some View {
    
    VStack {
        Rectangle()
            .frame(width: 200, height: animate ? 60 : 300)
            .foregroundColor(.blue)
            .onTapGesture {
                withAnimation(animation) {
                    animate.toggle()
                }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的主应用程序中,我能够将第一个任务视图(关闭)替换为另一个打开的视图。这可行,但看起来很糟糕,而且并没有真正做到我想要的。它使用淡入淡出动画有效地将视图替换为另一个视图。

ForEach(taskArrayHigh) { task in
if animate == false {
    TaskView(taskTitle: task.title, category: task.category?.rawValue ?? "", complete: task.complete?.rawValue ?? "", priorityColor: Color("HighPriority"), task: task, activeDate: activeDate)
        .padding(.top, 10)
        .padding(.horizontal)
        .onTapGesture {
            withAnimation(.easeIn) {
                animate.toggle()
            }
        }
        .transition(.move(edge: .bottom))
} else if animate == true {
    TaskViewOpen(task: "Grocery Shopping", category: "Home", remaining: 204, completed: 4)
        .padding(.top, 10)
        .padding(.horizontal)
        .onTapGesture {
            withAnimation(.easeIn) {
                animate.toggle()
            }
        }
}
Run Code Online (Sandbox Code Playgroud)

有没有办法使我原来的关闭视图动画化以打开并显示描述文本和按钮?

jn_*_*pdx 9

您的线路走在正确的轨道上.transition,但您希望确保容器保持不变并且内容发生变化 - 现在,您正在替换整个视图。

下面是一个简单的例子来说明这个概念:

struct ContentView: View {
    
    @State var isExpanded = false
    
    var body: some View {
        VStack {
            Text("Headline")
            if isExpanded {
                Text("More Info")
                Text("And more")
            }
        }
        .padding()
        .frame(maxWidth: .infinity)
        .transition(.move(edge: .bottom))
        .background(Color.gray.cornerRadius(10.0))
        .onTapGesture {
            withAnimation {
                isExpanded.toggle()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

由于您在 a 内部使用它ForEach,您可能希望将其抽象为它自己的组件,因为它需要自己的组件@State来跟踪扩展状态,如我在此处所示。


根据评论更新

使用 PreferenceKey 获取可扩展视图的高度的示例,以便框架可以进行动画处理并且没有任何淡入和淡出:

struct ContentView: View {
    
    @State var isExpanded = false
    @State var subviewHeight : CGFloat = 0
    
    var body: some View {
        VStack {
            Text("Headline")
            VStack {
                Text("More Info")
                Text("And more")
                Text("And more")
                Text("And more")
                Text("And more")
                Text("And more")
            }
        }
        .background(GeometryReader {
            Color.clear.preference(key: ViewHeightKey.self,
                                   value: $0.frame(in: .local).size.height)
        })
        .onPreferenceChange(ViewHeightKey.self) { subviewHeight = $0 }
        .frame(height: isExpanded ? subviewHeight : 50, alignment: .top)
        .padding()
        .clipped()
        .frame(maxWidth: .infinity)
        .transition(.move(edge: .bottom))
        .background(Color.gray.cornerRadius(10.0))
        .onTapGesture {
            withAnimation(.easeIn(duration: 2.0)) {
                isExpanded.toggle()
            }
        }
    }
}

struct ViewHeightKey: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = value + nextValue()
    }
}
Run Code Online (Sandbox Code Playgroud)