swift 使用NavigationLink和ObservableObject的断开动画

lf3rwulv  于 2023-10-15  发布在  Swift
关注(0)|答案(1)|浏览(118)

我有一个SwiftUI父视图,它在导航堆栈上推送一个细节视图。详细视图绑定到父视图上的某些数据。
根据设置的不同,在局部视图中,数据更改时发生的某些动画会被打断。SwiftUI中损坏的动画不仅丑陋,而且可能指向可能导致渲染性能的更深层次问题(请参阅https://developer.apple.com/wwdc21/10022)。
具体而言,该问题仅在以下情况下发生:

  • 我传递一个绑定到ObservableObject上的一些数据;* 和 *
  • 详细视图被推到NavigationStack.

它对所有其他情况都能正常工作。
我发现了一个解决方法,将ObservableObject传递给子视图,而不是绑定到data属性。然而,我不认为这是一个可行的解决方案,因为它是一个架构上的妥协,将不需要的信息暴露给细节视图(还有其他原因)。
这里是一个最小的可重复的例子。

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()
    }
}

我该怎么办?

uajslkp6

uajslkp61#

我不知道为什么会出现这个问题,但有一个简单的解决方法。
.animation添加到Cell中的VStack

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
    }
}

相关问题