我有一个SwiftUI父视图,它将细节视图推送到导航堆栈上.局部视图具有与父视图上的某些数据的绑定.
根据设置的不同,数据更改时发生的一些动画会在局部视图上中断.SwiftUI中损坏的动画不仅难看,而且可能指向可能导致渲染性能的更深层次问题(参见https://developer.apple.com/wwdc21/10022).
具体地说,只有在以下情况下才会出现问题:
- 我将绑定传递给
ObservableObject
;and上的一些数据 - 将局部视图推送到
NavigationStack.
对于所有其他情况,它的工作情况与预期一样.
我找到了一种解决方法,将ObservableObject
向下传递给子视图,而不是绑定到数据属性.然而,我不认为这是一个可行的解决方案,因为它是通过将不需要的信息expose 给细节视图(以及其他原因)而导致的架构折衷.
Here is a minimal reproducible example.
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()
}
}
我怎么才能解决这个问题呢?