我有一个SwiftUI父视图,它将细节视图推送到导航堆栈上.局部视图具有与父视图上的某些数据的绑定.

根据设置的不同,数据更改时发生的一些动画会在局部视图上中断.SwiftUI中损坏的动画不仅难看,而且可能指向可能导致渲染性能的更深层次问题(参见https://developer.apple.com/wwdc21/10022).

具体地说,只有在以下情况下才会出现问题:

  • 我将绑定传递给ObservableObjectand上的一些数据
  • 将局部视图推送到NavigationStack.

对于所有其他情况,它的工作情况与预期一样.

我找到了一种解决方法,将ObservableObject向下传递给子视图,而不是绑定到数据属性.然而,我不认为这是一个可行的解决方案,因为它是通过将不需要的信息expose 给细节视图(以及其他原因)而导致的架构折衷.

Here is a minimal reproducible example.
screenshot

import SwiftUI

struct SpaceProbe {
    // let id: UUID = UUID() // does not help
    let name: String
    var launched: Bool = false
}

class SpacePort: ObservableObject {
    @Published var probe: SpaceProbe = SpaceProbe(name: "Psyche", launched: true)
}

struct ContentView: View {
    @StateObject var spacePort: SpacePort = SpacePort()
    @State var probe: SpaceProbe = SpaceProbe(name: "Psyche", launched: true)
    var body: some View {
        NavigationStack {
            List {
                Section(header: Text("State:")) {
                    NavigationLink {
                        Cell(probe: $probe)
                            .padding()
                    } label: {
                        Text("Details (works)")
                    }
                    Cell(probe: $probe)
                }
                Section(header: Text("State Object:")) {
                    NavigationLink {
                        Cell(probe: $spacePort.probe)
                            .padding()
                    } label: {
                        Text("Details (broken animation)")
                    }
                    NavigationLink {
                        ObjectCell(spacePort: spacePort)
                            .padding()
                    } label: {
                        Text("Details (works but bad architecture)")
                    }
                    Cell(probe: $spacePort.probe)
                }

            }
            .navigationBarTitle("Space Probe")
        }
    }
}

struct Cell: View {
    @Binding var probe: SpaceProbe
    var body: some View {
        VStack(alignment: probe.launched ? .trailing : .leading) {
            HStack {
                Text(probe.name)
                Spacer()
                Button(probe.launched ? "Cancel" : "Launch") {
                    Task { @MainActor in
                        withAnimation {
                            probe.launched.toggle()
                        }
                    }
                }
            }
            Text("🛰️").font(.system(size: 40))
                .padding()
        }
    }
}

struct ObjectCell: View {
    @ObservedObject var spacePort: SpacePort
    var body: some View {
        VStack(alignment: spacePort.probe.launched ? .trailing : .leading) {
            HStack {
                Text(spacePort.probe.name)
                Spacer()
                Button(spacePort.probe.launched ? "Cancel" : "Launch") {
                    Task { @MainActor in
                        withAnimation {
                            spacePort.probe.launched.toggle()
                        }
                    }
                }
            }
            Text("🛰️").font(.system(size: 40))
                .padding()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

我怎么才能解决这个问题呢?

推荐答案

我不知道为什么会看到这个问题,但有一个简单的解决方法.

Cell中的VStack.animation:

struct Cell: View {
    @Binding var probe: SpaceProbe
    var body: some View {
        VStack(alignment: probe.launched ? .trailing : .leading) {
            // content as before
        }
        .animation(.easeInOut, value: probe.launched) // <- ADDED
    }
}

Swift相关问答推荐

如何在一个角色隔离类上编写自定义==实现?

同步问题,发送Api Call之前未设置idToken

如何将图像插入到矩形中

按下一步后无法让文本字段切换焦点

仅当单击UIButton时才调用计时器函数

对实例方法appendInterpolation的调用没有完全匹配

在表单中对齐文本框

Swift:结果的失败类型不能是协议 - Type 'any ShadowError' cannot conform to Error

将弱引用作为类函数引用传递时,弱引用无法按预期工作

如何在闭包中使用构造 await sync

为 ObservedObject 和 State 对象配置预览

RxSwift 事件触发了两次

如何更新 UserDefault 中的特定值

SwiftUI 视图的默认成员初始化器 VS 自定义初始化器

如何获得不同的插入和移除过渡动画?

Vapor 4,如何按外键过滤?

Vapor Swift 如何配置客户端连接超时

在 Swift 中,如何查看 Process() 传递给 shell 的字符串?

如何在Swift中找出字母是字母数字还是数字

Swift 5 秒后关闭 UIAlertView