ios - SwiftUI 列表未更新内拉以刷新 View

标签 ios swift xcode swiftui swift5

我在 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 对复制代码进行测试

demo

因此,自定义 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/

相关文章:

ios - 当用户在 Swift 中购买游戏元素时,如何在我的游戏中减去硬币?

xcode - 带有静态共享库的 Kiwi 和 CocoaPods

ios - TableView 不显示标签 ios

objective-c - 协议(protocol)中的方法定义

ios - 在 Swift(iOS)和 setValue forKey 中子类化 NSObject 的子类

ios - Swift:当有很多数据要显示时如何使用 ScrollView

iphone - 在设备上安装 iPhone 应用程序时出错

ios - Swift/如何使 MFMailComposeViewController 消失

ios - 带有 UISearchBar 的 MapKit

ios - CloudKit 的订阅不起作用