子视图中的 ListView 未正确刷新
Posted
技术标签:
【中文标题】子视图中的 ListView 未正确刷新【英文标题】:ListView in child view is not refreshed correctly 【发布时间】:2020-06-06 21:17:02 【问题描述】:有一个ListView。当我在列表中单击元素时,我通过更改元素的字段在 Cloud Firestore 中进行交易。数据库中的数据会按应有的方式更改,但在此操作之后,列表中的所有元素都会消失(尽管有 .onAppear fetchData)。重要的一点:这是子视图,父视图没有这个问题。
我还在列表底部添加了一个按钮来执行fetchData(),当我点击它时,数据返回到列表中
可能是什么问题?谢谢
import SwiftUI
struct SecondView: View
@ObservedObject var viewModel = BooksViewModel()
var body: some View
VStack
List(viewModel.books) book in
VStack(alignment: .leading)
Button("Update data")
let updBook = book
self.viewModel.myTransaction(book: updBook)
Text(book.title)
.font(.headline)
Text(book.author)
.font(.subheadline)
Text("\(book.numberOfPages) pages")
.font(.subheadline)
.navigationBarTitle("Books")
.onAppear()
self.viewModel.fetchData()
Button("update list")
self.viewModel.fetchData()
视图模型:
import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift
class BooksViewModel: ObservableObject
@Published var books = [Book]()
private var db = Firestore.firestore()
func fetchData()
db.collection("books").addSnapshotListener (querySnapshot, error) in
guard let documents = querySnapshot?.documents else
print("No documents")
return
self.books = documents.compactMap queryDocumentSnapshot -> Book? in
return try? queryDocumentSnapshot.data(as: Book.self)
func deleteBook(book: Book)
if let bookID = book.id
db.collection("books").document(bookID).delete()
func updateBook(book: Book)
if let bookID = book.id
do
try db.collection("books").document(bookID).setData(from: book)
catch
print(error)
func addBook(book: Book)
do
let _ = try db.collection("books").addDocument(from: book)
catch
print(error)
func myTransaction(book: Book)
let bookID = book.id
let targetReference = db.collection("books").document(bookID!)
db.runTransaction( (transaction, errorPointer) -> Any? in
let targetDocument: DocumentSnapshot
do
try targetDocument = transaction.getDocument(targetReference)
catch let fetchError as NSError
errorPointer?.pointee = fetchError
return nil
guard let oldValue = targetDocument.data()?["pages"] as? Int else
let error = NSError(
domain: "AppErrorDomain",
code: -1,
userInfo: [
NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(targetDocument)"
]
)
errorPointer?.pointee = error
return nil
// Note: this could be done without a transaction
// by updating the population using FieldValue.increment()
transaction.updateData(["pages": oldValue + 1], forDocument: targetReference)
return nil
) (object, error) in
if let error = error
print("Transaction failed: \(error)")
else
print("Transaction successfully committed!")
父视图:
import SwiftUI
struct ContentView: View
@ObservedObject var viewModel = BooksViewModel()
var body: some View
NavigationView
VStack
List(viewModel.books) book in
VStack(alignment: .leading)
Button("Update")
let delBook = book
self.viewModel.myTransaction(book: delBook)
Text(book.title)
.font(.headline)
Text(book.author)
.font(.subheadline)
Text("\(book.numberOfPages) pages")
.font(.subheadline)
.navigationBarTitle("Books")
.onAppear()
self.viewModel.fetchData()
NavigationLink(destination: SecondView())
Text("Second View")
【问题讨论】:
你能显示你的BooksViewModel
的代码吗?
@pawello2222 当然可以。我更新了问题
你试过用List ForEach(viewModel.books, id: \.self) book in ...
代替List(viewModel.books)
吗?
@pawello2222 出现错误:“在 'ForEach' 上引用初始化程序 'init(_:id:content:)' 要求 'Book' 符合 'Hashable'”。但是,如果我删除第二个参数 (id:),错误就会消失,问题仍然存在
动态列表必须使用id
参数。如果可以,请尝试将您的 Book
与 Hashable
保持一致 (struct Book: Hashable ...
)。
【参考方案1】:
一个可能的解决方案可能是您的视图和它的视图模型相互干扰。看起来您创建了相同 BookViewModel
的两个实例:
struct ContentView: View
@ObservedObject var viewModel = BooksViewModel()
struct SecondView: View
@ObservedObject var viewModel = BooksViewModel()
尝试创建一个BooksViewModel
并在视图之间传递它(您可以使用@EnvironmentObject
)。
【讨论】:
以上是关于子视图中的 ListView 未正确刷新的主要内容,如果未能解决你的问题,请参考以下文章