我有以下 SwiftUI 视图:
struct ProductView: View {
@ObservedObject var productViewModel: ProductViewModel
var body: some View {
VStack {
ZStack(alignment: .top) {
if(self.productViewModel.product != nil) {
URLImage(url: self.productViewModel.product!.imageurl, itemColor: self.productViewModel.selectedColor)
}
else {
Image("loading")
}
}
}
}
观察 ProductViewModel
class ProductViewModel: ObservableObject {
@Published var selectedColor: UIColor = .white
@Published var product: Product?
private var cancellable: AnyCancellable!
init(productFuture: Future<Product, Never>) {
self.cancellable = productFuture.sink(receiveCompletion: { comp in
print(comp)
}, receiveValue: { product in
self.product = product
print(self.product) // this prints the expected product. The network call works just fine
})
}
Product 是一个包含多个字符串属性的 Swift 结构体:
struct Product {
let id: String
let imageurl: String
let price: String
}
它是从远程 API 获取的。执行获取的服务返回一个组合未来并将其传递给视图模型,如下所示:
let productFuture = retrieveProduct(productID: "1")
let productVM = ProductViewModel(productFuture: productFuture)
let productView = ProductView(productViewModel: productViewModel)
func retrieveProduct(productID: String) -> Future<Product, Never>{
let future = Future<Product, Never> { promise in
// networking logic that fetches the remote product, once it finishes the success callback is invoked
promise(.success(product))
}
return future
}
为简洁起见,我排除了网络和错误处理逻辑,因为它与手头的案例无关。为了尽快重现这一点,只需使用一些虚拟值初始化一个模拟产品,并将其传递给成功回调,延迟如下:
let mockproduct = Product(id: "1", imageurl: "https://exampleurl.com", price: "$10")
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
promise(.success(mockproduct))
})
一旦产品通过网络到达,它就会被分配给已发布的产品属性。提取工作并将正确的值分配给已发布的属性。显然,这发生在创建视图之后,因为网络调用需要一些时间。但是,即使发布的对象已更改,视图也不会更新。
当我直接通过视图模型初始化程序而不是未来传递产品时,它按预期工作并且视图显示正确的产品。
关于为什么视图在通过组合未来异步更新时视图模型状态的变化没有反应的任何建议?
编辑:当我问这个问题时,我将 ProductViewModel + ProductView 嵌套在另一个视图中。所以基本上productview只是一个更大的CategoryView的一部分。CategoryViewmodel 以专用方法初始化 ProductViewModel 和 ProductView:
func createProductView() -> AnyView {
let productVM = productViewModels[productIndex]
return AnyView(ProductView(productViewModel: productVM))
}
然后在每次更新时由 CategoryView 调用。我猜这让嵌套 ProductViewModel 中的 Published 变量无法正确更新,因为从 CategoryView 向下的视图层次结构在每次更新时都会重建。因此,每次更新都会调用 createProductView 方法,从而对 ProductView + ProductViewModel 进行全新的初始化。
也许对 SwiftUI 有更多经验的人可以对此发表评论。
在嵌套视图中嵌套可观察对象通常是一个坏主意,还是有办法使这项工作不是反模式?
如果没有,当你有嵌套视图并且每个视图都有自己的状态时,你通常如何解决这个问题?