我在 Custom Pull 中有一个 List 来刷新 View 。当 SeeAllViewModel 中的数组更新时, View 中的这个列表不会被更新。不仅如此,计数器也没有更新。当我把列表放在这个之外时 CustomScrollView
它更新得很好。所以我猜我的 CustomScrollView
有问题.知道为什么会这样吗?此外,我将为我的 ViewModel 提供代码,以防万一。
struct SeeAllView: View {
@ObservedObject var seeAllViewModel: SeeAllViewModel
var body: some View {
GeometryReader { geometry in
VStack {
Text("\(self.seeAllViewModel.category.items.count)") // updated on refresh
CustomScrollView(width: geometry.size.width, height: geometry.size.height, viewModel: self.seeAllViewModel) {
VStack {
Text("\(self.seeAllViewModel.category.items.count)") // not being updated
List {
ForEach(self.seeAllViewModel.category.items) { (item: Item) in
ItemRowView(itemViewModel: ItemViewModel(item: item))
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle(Text(self.seeAllViewModel.category.title.firstCapitalized))
}
}
Button(action: {
self.seeAllViewModel.refresh()
}) { Text("refresh")
}
}
}
}
}
自定义 ScrollView struct CustomScrollView<Content: View, VM: LoadProtocol> : UIViewRepresentable {
var width : CGFloat
var height : CGFloat
let viewModel: VM
let content: () -> Content
func makeCoordinator() -> Coordinator {
Coordinator(self, viewModel: viewModel)
}
func makeUIView(context: Context) -> UIScrollView {
let control = UIScrollView()
control.refreshControl = UIRefreshControl()
control.refreshControl?.addTarget(context.coordinator, action: #selector(Coordinator.handleRefreshControl), for: .valueChanged)
let childView = UIHostingController(rootView: content())
childView.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
control.addSubview(childView.view)
return control
}
func updateUIView(_ uiView: UIScrollView, context: Context) { }
class Coordinator: NSObject {
var control: CustomScrollView<Content, VM>
var viewModel: VM
init(_ control: CustomScrollView, viewModel: VM) {
self.control = control
self.viewModel = viewModel
}
@objc func handleRefreshControl(sender: UIRefreshControl) {
sender.endRefreshing()
viewModel.refresh()
}
}
}查看所有 View 模型
class SeeAllViewModel: ObservableObject, LoadProtocol {
@Published var category: Category
init(category: Category) {
self.category = category
}
func refresh() {
//everytime you need more data fetched and on database updates to your snapshot this will be triggered
let query = self.category.query.start(afterDocument: self.category.lastDocumentSnapshot!).limit(to: 1)
query.addSnapshotListener { (snapshot, error) in
guard let snapshot = snapshot else {
print("Error retrieving cities: \(error.debugDescription)")
return
}
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
return
}
self.category.lastDocumentSnapshot = lastSnapshot
// Construct a new query starting after this document,
// Use the query for pagination.
self.category.items += snapshot.documents.map { document -> Item in
return Item(document: document)
}
}
}
}
最佳答案
似乎动态属性更新无法通过不同宿主 Controller 的边界,因此解决方案是将其(在这种情况下为可观察对象)显式传递到内部。
使用 Xcode 11.4/iOS 13.4 对复制代码进行测试
因此,自定义 View 构造为
CustomScrollView(width: geometry.size.width, height: geometry.size.height, viewModel: self.seeAllViewModel) {
// Separate internals to subview and pass view modal there
RefreshInternalView(seeAllViewModel: self.seeAllViewModel)
}
这是单独的 View ,没什么特别的 - 只是从上面提取了所有内容
struct RefreshInternalView: View {
@ObservedObject var seeAllViewModel: SeeAllViewModel
var body: some View {
VStack {
Text("\(self.seeAllViewModel.category.items.count)") // not being updated
List {
ForEach(self.seeAllViewModel.category.items) { (item: Item) in
ItemRowView(itemViewModel: ItemViewModel(item: item))
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle(Text(self.seeAllViewModel.category.title.firstCapitalized))
}
}
}
关于ios - SwiftUI 列表未更新内拉以刷新 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61897919/