问题
I have a ObservableObject
that I use to update my UI when new data is sent from the server (an a class which contains an array of custom structs).
For some reason, when the data is sent, the ContentView
's body
is called, but the data isn't changed. I even added a print
statement to check if the data that the array contains is right and it is.
When I try to switch to another tab on my TabView
, and then switch back to the main view, the UI does get updated. Does anybody know why the UI updates just when I switch tabs, although the body
gets recalled to update the UI when the data changed?
HomeView
struct HomeView: View {
@ObservedObject private var fbData = firebaseData
var body: some View {
TabView {
//Home Tab
NavigationView {
ScrollView(showsIndicators: false) {
ForEach(self.fbData.posts.indices, id: \.self) { postIndex in
PostView(post: self.$fbData.posts[postIndex])
.listRowInsets(EdgeInsets())
.padding(.vertical, 5)
}
}
.navigationBarTitle("MyPhotoApp", displayMode: .inline)
.navigationBarItems(leading:
Button(action: {
print("Camera btn pressed")
}, label: {
Image(systemName: "camera")
.font(.title)
})
, trailing:
Button(action: {
print("Messages btn pressed")
}, label: {
Image(systemName: "paperplane")
.font(.title)
})
)
} . tabItem({
Image(systemName: "house")
.font(.title)
})
Text("Search").tabItem {
Image(systemName: "magnifyingglass")
.font(.title)
}
Text("Upload").tabItem {
Image(systemName: "plus.app")
.font(.title)
}
Text("Activity").tabItem {
Image(systemName: "heart")
.font(.title)
}
Text("Profile").tabItem {
Image(systemName: "person")
.font(.title)
}
}
.accentColor(.black)
.edgesIgnoringSafeArea(.top)
}
}
FirebaseData:
class FirebaseData : ObservableObject {
@Published var posts = [Post]()
let postsCollection = Firestore.firestore().collection("Posts")
init() {
self.fetchPosts()
}
//MARK: Fetch Data
private func fetchPosts() {
self.postsCollection.addSnapshotListener { (documentSnapshot, err) in
if err != nil {
print("Error fetching posts: \(err!.localizedDescription)")
return
} else {
documentSnapshot!.documentChanges.forEach { diff in
if diff.type == .added {
let post = self.createPostFromDocument(document: diff.document)
self.posts.append(post)
} else if diff.type == .modified {
self.posts = self.posts.map { (post) -> Post in
if post.id == diff.document.documentID {
return self.createPostFromDocument(document: diff.document)
} else {
return post
}
}
} else if diff.type == .removed {
for index in self.posts.indices {
if self.posts[index].id == diff.document.documentID {
self.posts.remove(at: index)
}
}
}
}
}
}
}
回答1:
Your code example doesn't help to find the bug. Finally I've got how to demonstrate it. First, do it the "proper way" (copy - paste - try it yourself)
import SwiftUI
struct Data: Identifiable {
let id = UUID()
let text: String
}
class Model: ObservableObject {
@Published var data: [Data] = [Data(text: "alfa"), Data(text: "beta")]
}
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
TabView {
View1(model: model).tabItem {
Text("View 1")
}
View2(model: model).tabItem {
Text("View 2")
}
}
}
}
struct View1: View {
@ObservedObject var model: Model
var body: some View {
VStack {
Text("View 1").font(.largeTitle)
DataView(data: model.data)
Button(action: {
self.model.data.append(Data(text: String("ABCDEFGH".shuffled())))
}) {
Text("Add random data")
}
}
}
}
struct View2: View {
@ObservedObject var model: Model
var body: some View {
VStack {
Text("View 2").font(.largeTitle)
DataView(data: model.data.filter({ (item) -> Bool in
item.text.count < 4
}))
// to distinguish from other DataView !! it seems to be a bug in SwiftUI
// try to remove it to see the difference
.id("view2")
Button(action: {
self.model.data.append(Data(text: String("ABC".shuffled())))
}) {
Text("Add random data")
}
}
}
}
struct DataView: View {
var data: [Data]
var body: some View {
List {
ForEach(data) { (item) in
Text(item.text)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I works as it should, you can modify the data from each tab, you see refreshed data, etc.
removing "fixed - user defined" .id modifier, it changes the behaviour dramatically
This looks like a serious bug in SwiftUI ...
回答2:
I think is SwiftUI bug. I solve this problem like this. Instead of rendering your PostView(post: self.$fbData.posts[postIndex]) implement post view inside ForEach.
ForEach(self.fbData.posts.indices, id: \.self) { postIndex in
Text(self.$fbData.posts[postIndex].comment)
Text(self.$fbData.posts[postIndex].date)
....
}
来源:https://stackoverflow.com/questions/60730835/ui-changes-with-observableobject-just-after-switching-tabs