使用 ObservableObject 时视图不会更新
View doesn't get updated when using ObservableObject
我正在尝试使用 Swift 构建一个 Instagram 克隆应用程序UI。
我正在通过 Firebase
获取数据,并尝试在每次服务器中的数据发生变化时实现 UI 更新。
出于某种原因,当我第一次打开应用程序并获取数据时,我的视图的 body
被调用,但 UI 没有改变。我什至设置了一个断点,看到 body
被调用并包含正确的信息,只是 UI 没有得到更新。
我的应用程序中有几个选项卡,当我切换到另一个选项卡时(除了 Text
之外什么都没有),突然 UI 是否更新。
请看下面的动图:
这是我的代码:
主视图:
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("Instagram", 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:
let firebaseData = 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)
}
}
}
}
}
}
}
private func createPostFromDocument(document: QueryDocumentSnapshot) -> Post {
let data = document.data()
let id = document.documentID
let imageUrl = data["imageUrl"] as! String
let authorUsername = data["authorUsername"] as! String
let authorProfilePictureUrl = data["authorProfilePictureUrl"] as! String
let postLocation = data["postLocation"] as! String
let postDescription = data["postDescription"] as! String
let numberOfLikes = data["numberOfLikes"] as! Int
let numberOfComments = data["numberOfComments"] as! Int
let datePosted = (data["datePosted"] as! Timestamp).dateValue()
let isLiked = data["isLiked"] as! Bool
return Post(id: id, imageUrl: imageUrl, authorUsername: authorUsername, authorProfilePictureUrl: authorProfilePictureUrl, postLocation: postLocation, postDescription: postDescription, numberOfLikes: numberOfLikes, numberOfComments: numberOfComments, datePosted: datePosted, isLiked: isLiked)
}
}
如果您需要我post更多代码,请告诉我。
更新:
Post查看:
struct PostView: View {
@Binding var post: Post
var body: some View {
VStack(alignment: .leading) {
//Info bar
HStack {
WebImage(url: URL(string: post.authorProfilePictureUrl))
.resizable()
.frame(width: 40, height: 40)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 2) {
Text(post.authorUsername).font(.headline)
Text(post.postLocation)
}
Spacer()
Button(action: {
print("More options pressed")
}, label: {
Image(systemName: "ellipsis")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
}
.padding(.horizontal)
//Main Image
WebImage(url: URL(string: post.imageUrl))
.resizable()
.aspectRatio(contentMode: .fit)
//Tools bar
HStack(spacing: 15) {
Button(action: {
self.post.isLiked.toggle()
print("Like btn pressed")
}, label: {
Image(systemName: post.isLiked ? "heart.fill" : "heart")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Button(action: {
print("Comments btn pressed")
}, label: {
Image(systemName: "message")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Button(action: {
print("Share btn pressed")
}, label: {
Image(systemName: "paperplane")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Spacer()
Button(action: {
print("Bookmark btn pressed")
}, label: {
Image(systemName: "bookmark")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
}.padding(8)
Text("Liked by \(post.numberOfLikes) users")
.font(.headline)
.padding(.horizontal, 8)
Text(post.postDescription)
.font(.body)
.padding(.horizontal, 8)
.padding(.vertical, 5)
Button(action: {
print("Show comments btn pressed")
}, label: {
Text("See all \(post.numberOfComments) comments")
.foregroundColor(.gray)
.padding(.horizontal, 8)
}).buttonStyle(BorderlessButtonStyle())
Text(post.datePostedString)
.font(.caption)
.foregroundColor(.gray)
.padding(.horizontal, 8)
.padding(.vertical, 5)
}
}
}
Post:
struct Post : Identifiable, Hashable {
var id: String
var imageUrl: String
var authorUsername: String
var authorProfilePictureUrl: String
var postLocation: String
var postDescription: String
var numberOfLikes: Int
var numberOfComments: Int
var datePostedString: String
var isLiked: Bool
init(id: String, imageUrl: String, authorUsername: String, authorProfilePictureUrl: String, postLocation: String, postDescription : String, numberOfLikes: Int, numberOfComments: Int, datePosted: Date, isLiked: Bool) {
self.id = id
self.imageUrl = imageUrl
self.authorUsername = authorUsername
self.authorProfilePictureUrl = authorProfilePictureUrl
self.postLocation = postLocation
self.postDescription = postDescription
self.numberOfLikes = numberOfLikes
self.numberOfComments = numberOfComments
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy"
self.datePostedString = dateFormatter.string(from: datePosted)
self.isLiked = isLiked
}
}
谢谢!
问题是,当应用程序启动时,您的数组为空,并且 ScrollView 停止更新,您可以将其替换为 VStack,它会工作(仅用于测试)。
解决方案是用条件包裹 ForEach
(或 ScrollView),如下所示:
if (fbData.posts.count > 0) {
ForEach(self.fbData.posts.indices, id: \.self) { postIndex in
PostView(post: self.$fbData.posts[postIndex])
.listRowInsets(EdgeInsets())
.padding(.vertical, 5)
}
}
我正在尝试使用 Swift 构建一个 Instagram 克隆应用程序UI。
我正在通过 Firebase
获取数据,并尝试在每次服务器中的数据发生变化时实现 UI 更新。
出于某种原因,当我第一次打开应用程序并获取数据时,我的视图的 body
被调用,但 UI 没有改变。我什至设置了一个断点,看到 body
被调用并包含正确的信息,只是 UI 没有得到更新。
我的应用程序中有几个选项卡,当我切换到另一个选项卡时(除了 Text
之外什么都没有),突然 UI 是否更新。
请看下面的动图:
这是我的代码:
主视图:
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("Instagram", 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:
let firebaseData = 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)
}
}
}
}
}
}
}
private func createPostFromDocument(document: QueryDocumentSnapshot) -> Post {
let data = document.data()
let id = document.documentID
let imageUrl = data["imageUrl"] as! String
let authorUsername = data["authorUsername"] as! String
let authorProfilePictureUrl = data["authorProfilePictureUrl"] as! String
let postLocation = data["postLocation"] as! String
let postDescription = data["postDescription"] as! String
let numberOfLikes = data["numberOfLikes"] as! Int
let numberOfComments = data["numberOfComments"] as! Int
let datePosted = (data["datePosted"] as! Timestamp).dateValue()
let isLiked = data["isLiked"] as! Bool
return Post(id: id, imageUrl: imageUrl, authorUsername: authorUsername, authorProfilePictureUrl: authorProfilePictureUrl, postLocation: postLocation, postDescription: postDescription, numberOfLikes: numberOfLikes, numberOfComments: numberOfComments, datePosted: datePosted, isLiked: isLiked)
}
}
如果您需要我post更多代码,请告诉我。
更新:
Post查看:
struct PostView: View {
@Binding var post: Post
var body: some View {
VStack(alignment: .leading) {
//Info bar
HStack {
WebImage(url: URL(string: post.authorProfilePictureUrl))
.resizable()
.frame(width: 40, height: 40)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 2) {
Text(post.authorUsername).font(.headline)
Text(post.postLocation)
}
Spacer()
Button(action: {
print("More options pressed")
}, label: {
Image(systemName: "ellipsis")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
}
.padding(.horizontal)
//Main Image
WebImage(url: URL(string: post.imageUrl))
.resizable()
.aspectRatio(contentMode: .fit)
//Tools bar
HStack(spacing: 15) {
Button(action: {
self.post.isLiked.toggle()
print("Like btn pressed")
}, label: {
Image(systemName: post.isLiked ? "heart.fill" : "heart")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Button(action: {
print("Comments btn pressed")
}, label: {
Image(systemName: "message")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Button(action: {
print("Share btn pressed")
}, label: {
Image(systemName: "paperplane")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
Spacer()
Button(action: {
print("Bookmark btn pressed")
}, label: {
Image(systemName: "bookmark")
.font(.title)
.foregroundColor(.black)
}).buttonStyle(BorderlessButtonStyle())
}.padding(8)
Text("Liked by \(post.numberOfLikes) users")
.font(.headline)
.padding(.horizontal, 8)
Text(post.postDescription)
.font(.body)
.padding(.horizontal, 8)
.padding(.vertical, 5)
Button(action: {
print("Show comments btn pressed")
}, label: {
Text("See all \(post.numberOfComments) comments")
.foregroundColor(.gray)
.padding(.horizontal, 8)
}).buttonStyle(BorderlessButtonStyle())
Text(post.datePostedString)
.font(.caption)
.foregroundColor(.gray)
.padding(.horizontal, 8)
.padding(.vertical, 5)
}
}
}
Post:
struct Post : Identifiable, Hashable {
var id: String
var imageUrl: String
var authorUsername: String
var authorProfilePictureUrl: String
var postLocation: String
var postDescription: String
var numberOfLikes: Int
var numberOfComments: Int
var datePostedString: String
var isLiked: Bool
init(id: String, imageUrl: String, authorUsername: String, authorProfilePictureUrl: String, postLocation: String, postDescription : String, numberOfLikes: Int, numberOfComments: Int, datePosted: Date, isLiked: Bool) {
self.id = id
self.imageUrl = imageUrl
self.authorUsername = authorUsername
self.authorProfilePictureUrl = authorProfilePictureUrl
self.postLocation = postLocation
self.postDescription = postDescription
self.numberOfLikes = numberOfLikes
self.numberOfComments = numberOfComments
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy"
self.datePostedString = dateFormatter.string(from: datePosted)
self.isLiked = isLiked
}
}
谢谢!
问题是,当应用程序启动时,您的数组为空,并且 ScrollView 停止更新,您可以将其替换为 VStack,它会工作(仅用于测试)。
解决方案是用条件包裹 ForEach
(或 ScrollView),如下所示:
if (fbData.posts.count > 0) {
ForEach(self.fbData.posts.indices, id: \.self) { postIndex in
PostView(post: self.$fbData.posts[postIndex])
.listRowInsets(EdgeInsets())
.padding(.vertical, 5)
}
}