gpt4 book ai didi

swift - 如何对@ObservedObject 的更改进行动画处理?

转载 作者:行者123 更新时间:2023-12-03 09:16:47 24 4
gpt4 key购买 nike

我的 View 由存储在 ViewModel 中的状态决定.有时 View 可能会调用其 ViewModel 上的函数,导致异步状态更改。

如何在 View 中为该状态更改的效果设置动画?

这是一个人为的例子,其中调用 viewModel.change()将导致 View 改变颜色。

  • 预期行为:从蓝色缓慢溶解到红色。
  • 实际行为:立即从蓝色变为红色。

  • class ViewModel: ObservableObject {

    @Published var color: UIColor = .blue

    func change() {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    self.color = .red
    }
    }
    }

    struct ContentView: View {

    @ObservedObject var viewModel = ViewModel()

    var body: some View {
    Color(viewModel.color).onAppear {
    withAnimation(.easeInOut(duration: 1.0)) {
    self.viewModel.change()
    }
    }
    }
    }

    如果我删除 ViewModel并将状态存储在 View 本身中,一切都按预期工作。然而,这不是一个很好的解决方案,因为我想将状态封装在 ViewModel 中。 .

    struct ContentView: View {

    @State var color: UIColor = .blue

    var body: some View {
    Color(color).onAppear {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    withAnimation(.easeInOut(duration: 1.0)) {
    self.color = .red
    }
    }
    }
    }
    }

    最佳答案

    使用 .animation()

    您可以使用 .animation(...)在正文内容或任何 subview 上,但它将为 View 的所有更改设置动画。

    让我们考虑一个示例,当我们通过 ViewModel 调用两个 API 并使用 .animation(.default)关于正文:

    import SwiftUI
    import Foundation

    class ArticleViewModel: ObservableObject {
    @Published var title = ""
    @Published var content = ""

    func fetchArticle() {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
    self?.title = "Article Title"
    }
    }

    func fetchContent() {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
    self?.content = "Content"
    }
    }
    }

    struct ArticleView: View {
    @ObservedObject var viewModel = ArticleViewModel()

    var body: some View {
    VStack(alignment: .leading) {
    if viewModel.title.isEmpty {
    Button("Load Article") {
    self.viewModel.fetchArticle()
    }
    } else {
    Text(viewModel.title).font(.title)

    if viewModel.content.isEmpty {
    Button("Load content...") {
    self.viewModel.fetchContent()
    }
    .padding(.vertical, 5)
    .frame(maxWidth: .infinity, alignment: .center)
    } else {
    Rectangle()
    .foregroundColor(Color.blue.opacity(0.2))
    .frame(height: 80)
    .overlay(Text(viewModel.content))
    }
    }
    }
    .padding()
    .frame(width: 300)
    .background(Color.gray.opacity(0.2))
    .animation(.default) // animate all changes of the view
    }
    }

    结果将是下一个:

    Animation modifier on body

    你可以看到我们在两个 Action 上都有动画。这可能是首选行为,但在某些情况下,您可能希望 控制每一个 Action 分别地。

    使用 .onReceive()

    假设我们希望在第一次 API 调用( fetchArticle )后为 View 设置动画,但在第二次调用( fetchContent )时 - 只需重绘没有动画的 View 。换句话说 - 当 title 时动画 View content 时收到但不动画 View 已收到。

    为了实现这一点,我们需要:
  • 创建单独的属性 @State var title = ""在 View 中。
  • 在整个 View 中使用这个新属性而不是viewModel.title .
  • 声明 .onReceive(viewModel.$title) { newTitle in ... } .当发布者( viewModel.$title )发送一个新值时,这个闭包将执行。在这一步中,我们可以控制 View 中的属性。在我们的例子中,我们将更新 title View 的属性。
  • 使用 withAnimation {...}在闭包内为更改设置动画。

  • 所以当 title时我们会有动画更新。同时收到新的 content ViewModel 的值我们的 View 只是在没有动画的情况下更新。

    struct ArticleView: View {
    @ObservedObject var viewModel = ArticleViewModel()
    // 1
    @State var title = ""

    var body: some View {
    VStack(alignment: .leading) {
    // 2
    if title.isEmpty {
    Button("Load Article") {
    self.viewModel.fetchArticle()
    }
    } else {
    // 2
    Text(title).font(.title)

    if viewModel.content.isEmpty {
    Button("Load content...") {
    self.viewModel.fetchContent()
    }
    .padding(.vertical, 5)
    .frame(maxWidth: .infinity, alignment: .center)
    } else {
    Rectangle()
    .foregroundColor(Color.blue.opacity(0.2))
    .frame(height: 80)
    .overlay(Text(viewModel.content))
    }
    }
    }
    .padding()
    .frame(width: 300)
    .background(Color.gray.opacity(0.2))
    // 3
    .onReceive(viewModel.$title) { newTitle in
    // 4
    withAnimation {
    self.title = newTitle
    }
    }
    }
    }

    结果将是下一个:

    Useing onReceive

    关于swift - 如何对@ObservedObject 的更改进行动画处理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59646189/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com