@plubled更改时,scrollview中的each不会更新

发布于 2025-02-06 17:49:47 字数 10438 浏览 2 评论 0原文

当commentViewModel评论更新时,我在ScrollView中的foreach不会更新。它已成功更新,但是由于某种原因,注释视图不会更新。我已经尝试了一切,但似乎找不到解决方案。 也许评论应该变成可用的或可编码的。但是我不能完全做这项工作。 我还尝试通过添加if语句或空文本来删除滚动浏览的机会。但这不是问题。 任何帮助都会有所帮助。

//These are the updated View

struct CommentView: View {
    @StateObject var commentViewModel = CommentViewModel()
    static let emptyScrollToString = "emptyScrollToString"
    @State var commentCommentUser = ""
    @State var showCommentComment = false
    @State var post: Post
    
    init(_ post: Post) {
        self.post = post
    }
    
    var body: some View {
        
        VStack {
            commentView
            
            Divider()
            
            if showCommentComment {
                HStack {
                    Text("Svarer \(commentCommentUser)")
                        .foregroundColor(.black)
                        .font(.system(size: 16))
                        .opacity(0.3)
                    
                    Spacer()
                    
                    Button {
                        withAnimation(Animation.spring().speed(2)) {
                            showCommentComment.toggle()
                        }
                    } label: {
                        Text("x")
                            .font(.system(size: 16))
                            .foregroundColor(.black)
                    }

                }
                .padding()
                .background(Color(r: 237, g: 237, b: 237))
            }
            
            BottomBar(post: post)
                .frame(minHeight: 50,maxHeight: 180)
                .fixedSize(horizontal: false, vertical: true)
                .shadow(radius: 60)
            
                .navigationBarTitle("Kommentar", displayMode: .inline)
        }
        .onAppear() {
            UINavigationBar.appearance().tintColor = UIColor(red: 20/255, green: 147/255, blue: 2/255, alpha: 1)
            commentViewModel.fetchComments(post: post)
        }
    }
    
    private var commentView: some View {
        ScrollView {
            ScrollViewReader { scrollViewProxy in
                VStack {
                    HStack{ Spacer() }
                        .id(Self.emptyScrollToString)
                    
                    ForEach(commentViewModel.comments, id: \.id) { comment in 
                        CommentCell(post: post, comment: comment, commentCommentUser: $commentCommentUser, showCommentComment: $showCommentComment)
                    }
                }
                .onReceive(Just(commentViewModel.comments.count)) { _ in // <-- here
                                    withAnimation(.easeOut(duration: 0.5)) {
                                        print("Scroll to top")
                                        scrollViewProxy.scrollTo(Self.emptyScrollToString, anchor: .bottom)
                                    }
                                }
            }
        }
    }
    
    public func uploadData(commentText: String) {
        guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {return}
        guard let id = post.id else {return}
        
        let data = ["fromId":uid, "commentText":commentText, "likes":0, "timestamp": Timestamp()] as [String : Any]
        
        FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
            .document().setData(data) { error in
                if error != nil {
                    print("failed to post comment", error ?? "")
                    return
                }
                
                print("Update")
                commentViewModel.fetchComments(post: post) //Gets error here
            }
    }
    
}

struct BottomBar: View {
    var commentView: CommentView
    
    init(post: Post) {
        self.commentView = CommentView(post)
    }
    
    var body: some View {
        bottomBar
    }
    
    private var bottomBar: some View {
            HStack{
                TextEditorView(string: $commentText)
                    .overlay(RoundedRectangle(cornerRadius: 12)
                        .stroke(lineWidth: 1)
                        .opacity(0.5))
                
                VStack {
                    Spacer()
                    Button {
                        commentView.uploadData() // This also reset all @State variables in Commentview, for some reason
                        commentText = ""
                    } label: {
                        Text("Slå op")
                            .font(.system(size: 20, weight: .semibold))
                            .opacity(commentText.isEmpty ? 0.5 : 1)
                            .foregroundColor(Color(r: 20, g: 147, b: 2))
                    }
                    .padding(.bottom, 10)
                }

            }
            .padding()
    }
}

struct Comment: Identifiable, Decodable {
    @DocumentID var id: String?
    let commentText: String
    let fromId: String
    var likes: Int
    let timestamp: Timestamp

    var user: PostUser?
    var didLike: Bool? = false
}

class CommentViewModel: ObservableObject {
    @Published var comments = [Comment]()
    @Published var count = 0
    let service: CommentService
    let userService = UserService()

    init(post: Post) {
        self.service = CommentService(post: post)
        fetchComments()
    }

    func fetchComments() {
        service.fetchComments { comments in
            self.comments = comments
            self.count = self.comments.count

            for i in 0 ..< comments.count {
                let uid = comments[i].fromId

                self.userService.fetchUser(withUid: uid) { user in
                    self.comments[i].user = user
                }
            }
        }
    }
}

struct CommentService {
    let post: Post

    func fetchComments(completion: @escaping([Comment]) -> Void) {
        guard let id = post.id else {return}

        FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
            .order(by: "timestamp", descending: true)
            .getDocuments { snapshot, error in
                if error != nil {
                    print("failed fetching comments", error ?? "")
                    return
                }

                guard let docs = snapshot?.documents else {return}
                do {
                    let comments = try docs.compactMap({ try  $0.data(as: Comment.self) })
                    print("COmplete")
                    completion(comments)
                }
                catch {
                    print("failed")
                }

            }
    }
 }

这是旧的景色

struct Comment: Identifiable, Decodable {
    @DocumentID var id: String?
    let commentText: String
    let fromId: String
    var likes: Int
    let timestamp: Timestamp
    
    var user: PostUser?
    var didLike: Bool? = false
}

class CommentViewModel: ObservableObject {
    @Published var comments = [Comment]()
    @Published var count = 0
    let service: CommentService
    let userService = UserService()
    
    init(post: Post) {
        self.service = CommentService(post: post)
        fetchComments()
    }
    
    func fetchComments() {
        service.fetchComments { comments in
            self.comments = comments
            self.count = self.comments.count
            
            for i in 0 ..< comments.count {
                let uid = comments[i].fromId
                
                self.userService.fetchUser(withUid: uid) { user in
                    self.comments[i].user = user
                }
            }
        }
    }
}

struct CommentService {
    let post: Post
    
    func fetchComments(completion: @escaping([Comment]) -> Void) {
        guard let id = post.id else {return}
        
        FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
            .order(by: "timestamp", descending: true)
            .getDocuments { snapshot, error in
                if error != nil {
                    print("failed fetching comments", error ?? "")
                    return
                }
                
                guard let docs = snapshot?.documents else {return}
                do {
                    let comments = try docs.compactMap({ try  $0.data(as: Comment.self) })
                    print("COmplete")
                    completion(comments)
                }
                catch {
                    print("failed")
                }

            }
    }
 }
 
 struct CommentView: View {
    @ObservedObject var commentViewModel: CommentViewModel
    static let emptyScrollToString = "emptyScrollToString"
    
    init(post: Post) {
        commentViewModel = CommentViewModel(post: post)
    }
    
    var body: some View {
        
        VStack {
            commentView
            Divider()
            BottomBar(post: commentViewModel.service.post)
                .frame(minHeight: 50,maxHeight: 180)
                .fixedSize(horizontal: false, vertical: true)
                .shadow(radius: 60)
            
                .navigationBarTitle("Kommentar", displayMode: .inline)
        }
        .onAppear() {
            UINavigationBar.appearance().tintColor = UIColor(red: 20/255, green: 147/255, blue: 2/255, alpha: 1)
        }
    }
    
    private var commentView: some View {
        ScrollView {
            ScrollViewReader { scrollViewProxy in
                VStack {
                    HStack{ Spacer() }
                        .id(Self.emptyScrollToString)
                    
                    ForEach(commentViewModel.comments, id: \.id) { comment in // Here should it update
                        let _ = print("Reload")
                        CommentCell(post: commentViewModel.service.post, comment: comment)
                    }
                }
                .onReceive(commentViewModel.$count) { _ in // It doesn't update here either
                            withAnimation(.easeOut(duration: 0.5)) {
                                print("Scroll to top")
                                scrollViewProxy.scrollTo(Self.emptyScrollToString, anchor: .bottom)
                            }
                        }
            }
        }
    }
 }

My ForEach in a Scrollview does not get updated when CommentViewModel comments get updated. It gets successfully updated, but for some reason, CommentView does not get updated. I have tried everything, but can't seem to find a solution.
Maybe Comment should become a Hashable or Codable. But I can't quite make this work.
I also tried removing the chance of Scrollview being empty, by adding an if statement or empty Text. But this was not the problem.
Any help would be helpfull.

//These are the updated View

struct CommentView: View {
    @StateObject var commentViewModel = CommentViewModel()
    static let emptyScrollToString = "emptyScrollToString"
    @State var commentCommentUser = ""
    @State var showCommentComment = false
    @State var post: Post
    
    init(_ post: Post) {
        self.post = post
    }
    
    var body: some View {
        
        VStack {
            commentView
            
            Divider()
            
            if showCommentComment {
                HStack {
                    Text("Svarer \(commentCommentUser)")
                        .foregroundColor(.black)
                        .font(.system(size: 16))
                        .opacity(0.3)
                    
                    Spacer()
                    
                    Button {
                        withAnimation(Animation.spring().speed(2)) {
                            showCommentComment.toggle()
                        }
                    } label: {
                        Text("x")
                            .font(.system(size: 16))
                            .foregroundColor(.black)
                    }

                }
                .padding()
                .background(Color(r: 237, g: 237, b: 237))
            }
            
            BottomBar(post: post)
                .frame(minHeight: 50,maxHeight: 180)
                .fixedSize(horizontal: false, vertical: true)
                .shadow(radius: 60)
            
                .navigationBarTitle("Kommentar", displayMode: .inline)
        }
        .onAppear() {
            UINavigationBar.appearance().tintColor = UIColor(red: 20/255, green: 147/255, blue: 2/255, alpha: 1)
            commentViewModel.fetchComments(post: post)
        }
    }
    
    private var commentView: some View {
        ScrollView {
            ScrollViewReader { scrollViewProxy in
                VStack {
                    HStack{ Spacer() }
                        .id(Self.emptyScrollToString)
                    
                    ForEach(commentViewModel.comments, id: \.id) { comment in 
                        CommentCell(post: post, comment: comment, commentCommentUser: $commentCommentUser, showCommentComment: $showCommentComment)
                    }
                }
                .onReceive(Just(commentViewModel.comments.count)) { _ in // <-- here
                                    withAnimation(.easeOut(duration: 0.5)) {
                                        print("Scroll to top")
                                        scrollViewProxy.scrollTo(Self.emptyScrollToString, anchor: .bottom)
                                    }
                                }
            }
        }
    }
    
    public func uploadData(commentText: String) {
        guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {return}
        guard let id = post.id else {return}
        
        let data = ["fromId":uid, "commentText":commentText, "likes":0, "timestamp": Timestamp()] as [String : Any]
        
        FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
            .document().setData(data) { error in
                if error != nil {
                    print("failed to post comment", error ?? "")
                    return
                }
                
                print("Update")
                commentViewModel.fetchComments(post: post) //Gets error here
            }
    }
    
}

struct BottomBar: View {
    var commentView: CommentView
    
    init(post: Post) {
        self.commentView = CommentView(post)
    }
    
    var body: some View {
        bottomBar
    }
    
    private var bottomBar: some View {
            HStack{
                TextEditorView(string: $commentText)
                    .overlay(RoundedRectangle(cornerRadius: 12)
                        .stroke(lineWidth: 1)
                        .opacity(0.5))
                
                VStack {
                    Spacer()
                    Button {
                        commentView.uploadData() // This also reset all @State variables in Commentview, for some reason
                        commentText = ""
                    } label: {
                        Text("Slå op")
                            .font(.system(size: 20, weight: .semibold))
                            .opacity(commentText.isEmpty ? 0.5 : 1)
                            .foregroundColor(Color(r: 20, g: 147, b: 2))
                    }
                    .padding(.bottom, 10)
                }

            }
            .padding()
    }
}

struct Comment: Identifiable, Decodable {
    @DocumentID var id: String?
    let commentText: String
    let fromId: String
    var likes: Int
    let timestamp: Timestamp

    var user: PostUser?
    var didLike: Bool? = false
}

class CommentViewModel: ObservableObject {
    @Published var comments = [Comment]()
    @Published var count = 0
    let service: CommentService
    let userService = UserService()

    init(post: Post) {
        self.service = CommentService(post: post)
        fetchComments()
    }

    func fetchComments() {
        service.fetchComments { comments in
            self.comments = comments
            self.count = self.comments.count

            for i in 0 ..< comments.count {
                let uid = comments[i].fromId

                self.userService.fetchUser(withUid: uid) { user in
                    self.comments[i].user = user
                }
            }
        }
    }
}

struct CommentService {
    let post: Post

    func fetchComments(completion: @escaping([Comment]) -> Void) {
        guard let id = post.id else {return}

        FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
            .order(by: "timestamp", descending: true)
            .getDocuments { snapshot, error in
                if error != nil {
                    print("failed fetching comments", error ?? "")
                    return
                }

                guard let docs = snapshot?.documents else {return}
                do {
                    let comments = try docs.compactMap({ try  $0.data(as: Comment.self) })
                    print("COmplete")
                    completion(comments)
                }
                catch {
                    print("failed")
                }

            }
    }
 }

This is the old views

struct Comment: Identifiable, Decodable {
    @DocumentID var id: String?
    let commentText: String
    let fromId: String
    var likes: Int
    let timestamp: Timestamp
    
    var user: PostUser?
    var didLike: Bool? = false
}

class CommentViewModel: ObservableObject {
    @Published var comments = [Comment]()
    @Published var count = 0
    let service: CommentService
    let userService = UserService()
    
    init(post: Post) {
        self.service = CommentService(post: post)
        fetchComments()
    }
    
    func fetchComments() {
        service.fetchComments { comments in
            self.comments = comments
            self.count = self.comments.count
            
            for i in 0 ..< comments.count {
                let uid = comments[i].fromId
                
                self.userService.fetchUser(withUid: uid) { user in
                    self.comments[i].user = user
                }
            }
        }
    }
}

struct CommentService {
    let post: Post
    
    func fetchComments(completion: @escaping([Comment]) -> Void) {
        guard let id = post.id else {return}
        
        FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
            .order(by: "timestamp", descending: true)
            .getDocuments { snapshot, error in
                if error != nil {
                    print("failed fetching comments", error ?? "")
                    return
                }
                
                guard let docs = snapshot?.documents else {return}
                do {
                    let comments = try docs.compactMap({ try  $0.data(as: Comment.self) })
                    print("COmplete")
                    completion(comments)
                }
                catch {
                    print("failed")
                }

            }
    }
 }
 
 struct CommentView: View {
    @ObservedObject var commentViewModel: CommentViewModel
    static let emptyScrollToString = "emptyScrollToString"
    
    init(post: Post) {
        commentViewModel = CommentViewModel(post: post)
    }
    
    var body: some View {
        
        VStack {
            commentView
            Divider()
            BottomBar(post: commentViewModel.service.post)
                .frame(minHeight: 50,maxHeight: 180)
                .fixedSize(horizontal: false, vertical: true)
                .shadow(radius: 60)
            
                .navigationBarTitle("Kommentar", displayMode: .inline)
        }
        .onAppear() {
            UINavigationBar.appearance().tintColor = UIColor(red: 20/255, green: 147/255, blue: 2/255, alpha: 1)
        }
    }
    
    private var commentView: some View {
        ScrollView {
            ScrollViewReader { scrollViewProxy in
                VStack {
                    HStack{ Spacer() }
                        .id(Self.emptyScrollToString)
                    
                    ForEach(commentViewModel.comments, id: \.id) { comment in // Here should it update
                        let _ = print("Reload")
                        CommentCell(post: commentViewModel.service.post, comment: comment)
                    }
                }
                .onReceive(commentViewModel.$count) { _ in // It doesn't update here either
                            withAnimation(.easeOut(duration: 0.5)) {
                                print("Scroll to top")
                                scrollViewProxy.scrollTo(Self.emptyScrollToString, anchor: .bottom)
                            }
                        }
            }
        }
    }
 }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

赠佳期 2025-02-13 17:49:47

@stateObject 属性包装器拥有您创建的对象,因此,一旦通过任何更改进行更新的视图,它就会保持活力。

@observedObject 属性包装器不拥有您创建的对象,因此它将通过任何更改在视图更新中重新创建,以这种方式,属性观察者会丢失并且将无法接收更改。

因此,将ViewModel从 @ObseverObject 更改为 @stateObject 将解决问题。

@StateObject property wrapper own the object you created, so it will keep alive once View updated by any changes.

@ObservedObject property wrapper doesn't own the object you created, so it will recreated on View update by any changes, in this way property observer get lost and will not be able to receive changes.

So, changing your ViewModel from @ObservedObject to @StateObject will fix the issue.

·深蓝 2025-02-13 17:49:47

编辑1:考虑新的代码。

请注意,重要的是要对Swiftui基础知识有良好的掌握,尤其是如何使用和通过observableObject以及如何使用views。我建议您再次进行教程。

我试图修改您的代码,以使您了解如何重新结构它。支付细节,希望它有所帮助。

请注意,我已经评论了许多行,因为我没有firebase和您的其他代码,例如userService等...
调整我的代码以满足您的需求,并取消相关行。

import Foundation
import SwiftUI
import Combine

struct BottomBar: View {
    @ObservedObject var viewModel: CommentViewModel  // <-- here
    @State var post: Post
    @State var commentText = ""
    
    var body: some View {
        HStack {
            // TextEditorView(string: $commentText)
            TextEditor(text: $commentText) // <-- for testing
                .overlay(RoundedRectangle(cornerRadius: 12)
                    .stroke(lineWidth: 1)
                    .opacity(0.5))
            
            VStack {
                Spacer()
                Button {
                    // -- here
                    viewModel.uploadData(post: post, commentText: commentText)
                    commentText = ""
                } label: {
                    Text("Slå op")
                        .font(.system(size: 20, weight: .semibold))
                        .opacity(commentText.isEmpty ? 0.5 : 1)
                }
                .padding(.bottom, 10)
            }
        }
    }
}

struct CommentView: View {
    @StateObject var commentViewModel = CommentViewModel()
    
    static let emptyScrollToString = "emptyScrollToString"
    
    @State var commentCommentUser = ""
    @State var showCommentComment = false
    @State var post: Post
    
    var body: some View {
        VStack {
            commentView
            Divider()
            if showCommentComment {
                HStack {
                    Text("Svarer \(commentCommentUser)")
                        .foregroundColor(.black)
                        .font(.system(size: 16))
                        .opacity(0.3)
                    Spacer()
                    Button {
                        withAnimation(Animation.spring().speed(2)) {
                            showCommentComment.toggle()
                        }
                    } label: {
                        Text("x")
                            .font(.system(size: 16))
                            .foregroundColor(.black)
                    }
                }.padding()
            }
            BottomBar(viewModel: commentViewModel, post: post)
                .frame(minHeight: 50,maxHeight: 180)
                .fixedSize(horizontal: false, vertical: true)
                .shadow(radius: 60)
                .navigationBarTitle("Kommentar", displayMode: .inline)
        }
        .onAppear() {
            UINavigationBar.appearance().tintColor = UIColor(red: 20/255, green: 147/255, blue: 2/255, alpha: 1)
            commentViewModel.fetchComments(post: post)
        }
    }
    
    private var commentView: some View {
        ScrollView {
            ScrollViewReader { scrollViewProxy in
                VStack {
                    Spacer()
                    ForEach(commentViewModel.comments, id: \.id) { comment in
                        CommentCell(post: post, comment: comment, commentCommentUser: $commentCommentUser, showCommentComment: $showCommentComment)
                    }
                }
                .onReceive(Just(commentViewModel.comments.count)) { _ in // <-- here
                    withAnimation(.easeOut(duration: 0.5)) {
                        print("Scroll to top")
                        scrollViewProxy.scrollTo(Self.emptyScrollToString, anchor: .bottom)
                    }
                }
            }
        }
    }
    
}

// for testing
struct CommentCell: View {
    @State var post: Post
    @State var comment: Comment
    @Binding var commentCommentUser: String
    @Binding var showCommentComment: Bool
    
    var body: some View {
        Text(comment.commentText)  // for testing
    }
}

struct Comment: Identifiable, Decodable {
    //    @DocumentID
    var id: String?  // for testing
    let commentText: String
    let fromId: String
    var likes: Int
    //    let timestamp: Timestamp  // for testing
    
    var user: PostUser?
    var didLike: Bool? = false
}

class CommentViewModel: ObservableObject {
    @Published var comments = [Comment]()
    
    let service = CommentService()  // <-- here
    // let userService = UserService()  // for testing
    
    init() { } // <-- here
    
    func fetchComments(post: Post) { // <-- here
        service.fetchComments(post: post) { comments in
            self.comments = comments
            for i in 0 ..< comments.count {
                let uid = comments[i].fromId
                //                self.userService.fetchUser(withUid: uid) { user in
                //                    self.comments[i].user = user
                //                }
            }
        }
    }
    
    func uploadData(post: Post, commentText: String) {
        service.uploadData(post: post, commentText: commentText) { isGood in
            if isGood {
                self.fetchComments(post: post)
            }
        }
    }
    
}

struct CommentService {

    func fetchComments(post: Post, completion: @escaping([Comment]) -> Void) {
        guard let id = post.id else {completion([]); return}  // <-- here
        // FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
        //            .order(by: "timestamp", descending: true)
        //            .getDocuments { snapshot, error in
        //                if error != nil {
        //                    print("failed fetching comments", error ?? "")
        //                    return
        //                }
        //                guard let docs = snapshot?.documents else {return}
        //                do {
        //                    let comments = try docs.compactMap({ try  $0.data(as: Comment.self) })
        //                    print("COmplete")
        //                    completion(comments)
        //                }
        //                catch {
        //                    print("failed")
        //                    completion([])
        //                }
        //            }
    }
    
    func uploadData(post: Post, commentText: String, completion: @escaping(Bool) -> Void) {
        
        completion(true)  // for testing, to be removed
        
        //        guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {completion(false); return}  // <--- here
        //        guard let id = post.id else {completion(false); return}  // <--- here
        //
        //        let data = ["fromId":uid, "commentText":commentText, "likes":0, "timestamp": Timestamp()] as [String : Any]
        //   FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
        //            .document().setData(data) { error in
        //                if error != nil {
        //                    print("failed to post comment", error ?? "")
        //                    completion(false) // <--- here
        //                    return
        //                }
        //                print("Update")
        //                completion(true)  // <--- here
        //            }
    }
    
}

// for testing
struct PostUser: Identifiable, Decodable {
    var id: String?
}

// for testing
struct Post: Identifiable, Decodable {
    var id: String?
    var name = "something"
}

Edit-2:错字修复。

bottombar更改viewModel.Service.uploAddata(post:post,ryventtext:commenttext){_ in}
viewModel.uploaddata(帖子:post,ryventtext:commenttext)

EDIT-1: Taking your new code into consideration.

Note, it is important to have a good grip on the SwiftUI basics, especially how to use and pass ObservableObject and how to use Views. I suggest you do the tutorial again.

I have attempted to modify your code to give you an idea on how you could re-structure it. Pay atttention to the details, hope it helps.

Note, I have commented a number of lines, because I do not have Firebase and your other code, such as UserService etc...
Adjust my code to suit your needs, and uncomment the relevant lines.

import Foundation
import SwiftUI
import Combine

struct BottomBar: View {
    @ObservedObject var viewModel: CommentViewModel  // <-- here
    @State var post: Post
    @State var commentText = ""
    
    var body: some View {
        HStack {
            // TextEditorView(string: $commentText)
            TextEditor(text: $commentText) // <-- for testing
                .overlay(RoundedRectangle(cornerRadius: 12)
                    .stroke(lineWidth: 1)
                    .opacity(0.5))
            
            VStack {
                Spacer()
                Button {
                    // -- here
                    viewModel.uploadData(post: post, commentText: commentText)
                    commentText = ""
                } label: {
                    Text("Slå op")
                        .font(.system(size: 20, weight: .semibold))
                        .opacity(commentText.isEmpty ? 0.5 : 1)
                }
                .padding(.bottom, 10)
            }
        }
    }
}

struct CommentView: View {
    @StateObject var commentViewModel = CommentViewModel()
    
    static let emptyScrollToString = "emptyScrollToString"
    
    @State var commentCommentUser = ""
    @State var showCommentComment = false
    @State var post: Post
    
    var body: some View {
        VStack {
            commentView
            Divider()
            if showCommentComment {
                HStack {
                    Text("Svarer \(commentCommentUser)")
                        .foregroundColor(.black)
                        .font(.system(size: 16))
                        .opacity(0.3)
                    Spacer()
                    Button {
                        withAnimation(Animation.spring().speed(2)) {
                            showCommentComment.toggle()
                        }
                    } label: {
                        Text("x")
                            .font(.system(size: 16))
                            .foregroundColor(.black)
                    }
                }.padding()
            }
            BottomBar(viewModel: commentViewModel, post: post)
                .frame(minHeight: 50,maxHeight: 180)
                .fixedSize(horizontal: false, vertical: true)
                .shadow(radius: 60)
                .navigationBarTitle("Kommentar", displayMode: .inline)
        }
        .onAppear() {
            UINavigationBar.appearance().tintColor = UIColor(red: 20/255, green: 147/255, blue: 2/255, alpha: 1)
            commentViewModel.fetchComments(post: post)
        }
    }
    
    private var commentView: some View {
        ScrollView {
            ScrollViewReader { scrollViewProxy in
                VStack {
                    Spacer()
                    ForEach(commentViewModel.comments, id: \.id) { comment in
                        CommentCell(post: post, comment: comment, commentCommentUser: $commentCommentUser, showCommentComment: $showCommentComment)
                    }
                }
                .onReceive(Just(commentViewModel.comments.count)) { _ in // <-- here
                    withAnimation(.easeOut(duration: 0.5)) {
                        print("Scroll to top")
                        scrollViewProxy.scrollTo(Self.emptyScrollToString, anchor: .bottom)
                    }
                }
            }
        }
    }
    
}

// for testing
struct CommentCell: View {
    @State var post: Post
    @State var comment: Comment
    @Binding var commentCommentUser: String
    @Binding var showCommentComment: Bool
    
    var body: some View {
        Text(comment.commentText)  // for testing
    }
}

struct Comment: Identifiable, Decodable {
    //    @DocumentID
    var id: String?  // for testing
    let commentText: String
    let fromId: String
    var likes: Int
    //    let timestamp: Timestamp  // for testing
    
    var user: PostUser?
    var didLike: Bool? = false
}

class CommentViewModel: ObservableObject {
    @Published var comments = [Comment]()
    
    let service = CommentService()  // <-- here
    // let userService = UserService()  // for testing
    
    init() { } // <-- here
    
    func fetchComments(post: Post) { // <-- here
        service.fetchComments(post: post) { comments in
            self.comments = comments
            for i in 0 ..< comments.count {
                let uid = comments[i].fromId
                //                self.userService.fetchUser(withUid: uid) { user in
                //                    self.comments[i].user = user
                //                }
            }
        }
    }
    
    func uploadData(post: Post, commentText: String) {
        service.uploadData(post: post, commentText: commentText) { isGood in
            if isGood {
                self.fetchComments(post: post)
            }
        }
    }
    
}

struct CommentService {

    func fetchComments(post: Post, completion: @escaping([Comment]) -> Void) {
        guard let id = post.id else {completion([]); return}  // <-- here
        // FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
        //            .order(by: "timestamp", descending: true)
        //            .getDocuments { snapshot, error in
        //                if error != nil {
        //                    print("failed fetching comments", error ?? "")
        //                    return
        //                }
        //                guard let docs = snapshot?.documents else {return}
        //                do {
        //                    let comments = try docs.compactMap({ try  $0.data(as: Comment.self) })
        //                    print("COmplete")
        //                    completion(comments)
        //                }
        //                catch {
        //                    print("failed")
        //                    completion([])
        //                }
        //            }
    }
    
    func uploadData(post: Post, commentText: String, completion: @escaping(Bool) -> Void) {
        
        completion(true)  // for testing, to be removed
        
        //        guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {completion(false); return}  // <--- here
        //        guard let id = post.id else {completion(false); return}  // <--- here
        //
        //        let data = ["fromId":uid, "commentText":commentText, "likes":0, "timestamp": Timestamp()] as [String : Any]
        //   FirebaseManager.shared.firestore.collection("posts").document(id).collection("comments")
        //            .document().setData(data) { error in
        //                if error != nil {
        //                    print("failed to post comment", error ?? "")
        //                    completion(false) // <--- here
        //                    return
        //                }
        //                print("Update")
        //                completion(true)  // <--- here
        //            }
    }
    
}

// for testing
struct PostUser: Identifiable, Decodable {
    var id: String?
}

// for testing
struct Post: Identifiable, Decodable {
    var id: String?
    var name = "something"
}

EDIT-2: typo fix.

in BottomBar changed viewModel.service.uploadData(post: post, commentText: commentText) {_ in}
to viewModel.uploadData(post: post, commentText: commentText)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文