Ios 调用注释填充TableView:更正代码错误

Ios 调用注释填充TableView:更正代码错误,ios,json,swift,firebase,firebase-realtime-database,Ios,Json,Swift,Firebase,Firebase Realtime Database,我试图用我的帖子中的评论填充一个my commentsTable。我的数据库结构如下: { "posts" : { "-Lhu-XRs806sXSEQS2BF" : { "reports" : 0, "text" : "How can I improve my data structure?", "timestamp" : 1561120090116, "title" : "Hello Stack Exchange",

我试图用我的帖子中的评论填充一个my commentsTable。我的数据库结构如下:

  {
  "posts" : {
    "-Lhu-XRs806sXSEQS2BF" : {
      "reports" : 0,
      "text" : "How can I improve my data structure?",
      "timestamp" : 1561120090116,
      "title" : "Hello Stack Exchange",
      "userID" : "nyRBXSyyDhc1Qkypou0Iz0iMsyr1"
    },
    "-Lhu-fI6DMSZvy8EdIgM" : {
      "reports" : 0,
      "text" : "As in Libre",
      "timestamp" : 1561120126347,
      "title" : "Free",
      "userID" : "nyRBXSyyDhc1Qkypou0Iz0iMsyr1"
    },
    "comments" : {
      "-Lhu-hXISy-0N2V4ES-a" : {
        "reports" : 0,
        "timestamp" : 1561120135594,
        "userID" : "nyRBXSyyDhc1Qkypou0Iz0iMsyr1"
      },
      "-Lhu-j1cR6V407tyUYY1" : {
        "reports" : 0,
        "timestamp" : 1561120141801,
        "userID" : "nyRBXSyyDhc1Qkypou0Iz0iMsyr1"
      },
      "-Lhu-lrJp9H8SQowlYWz" : {
        "reports" : 0,
        "timestamp" : 1561120153314,
        "userID" : "nyRBXSyyDhc1Qkypou0Iz0iMsyr1"
      },
      "posts" : {
        "-Lhu-XRs806sXSEQS2BF" : {
          "comments" : {
            "-Lhu-hXISy-0N2V4ES-_" : "How is it going?",
            "-Lhu-j1cR6V407tyUYY0" : "It’s good to see you"
          }
        },
        "-Lhu-fI6DMSZvy8EdIgM" : {
          "comments" : {
            "-Lhu-lrJp9H8SQowlYWy" : "Richard Stallman"
          }
        }
      }
    }
  }
}
以及以下注释类:

class Comment {
    var id:String
    var text:String

    init(id: String, text:String) {
        self.id = id
        self.text = text
    }
}
在考虑了您的建议后,以下是我的代码:

var comments = [Comment] ()
@IBOutlet weak var commentsTable: UITableView!
@IBOutlet weak var commentPlaceHolder: UILabel!
@IBOutlet weak var newCommentLabel: UITextView!
weak var delegate:NewPostVCDelegate?
let ref = Database.database().reference().child("posts")

@IBAction func reply(_ sender: UIButton) {
    let userID = (Auth.auth().currentUser?.uid)!
    addComment(toPostId: post!.id, andComment: newCommentLabel.text, commentByUid: userID)
    loadComments(forPostId: post!.id)
    comments.removeAll()
    commentsTable.reloadData()
    newCommentLabel.text = String()
    commentPlaceHolder.isHidden = false
}

func addComment(toPostId: String, andComment: String, commentByUid: String) {
    let commentsRef = self.ref.child("comments") //ref to the comments node
    let thisCommentRef = commentsRef.child(toPostId) //ref to a node with postId as key
    let commentToAddRef = thisCommentRef.childByAutoId() //each comment will have it's own key
    let d = [
        "comment_text": andComment,
        "comment_by_uid": commentByUid]
    commentToAddRef.setValue(d)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    loadComments(forPostId: post!.id)
}

func loadComments(forPostId: String) {
    let ref = self.ref.child("comments")
    let thisPostRef = ref.child(forPostId)
    thisPostRef.observeSingleEvent(of: .value, with: { snapshot in

        let allComments = snapshot.children.allObjects as! [DataSnapshot]
        for commentSnap in allComments {
            let commenterUid = commentSnap.childSnapshot(forPath: "comment_by_uid").value as? String ?? "No uid"
            let commentText = commentSnap.childSnapshot(forPath: "comment_text").value as? String ?? "No comment"
            let aComment = Comment(id: commenterUid, text: commentText)
            self.comments.append(aComment)
            print(commenterUid, commentText)
        }
        self.commentsTable.reloadData()
    })
}

func adjustUITextViewHeight(arg : UITextView) {
    arg.translatesAutoresizingMaskIntoConstraints = true
    arg.sizeToFit()
    arg.isScrollEnabled = false
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.commentsTable.dataSource = self
    let cellNib = UINib(nibName: "CommentTableViewCell", bundle: nil)
    commentsTable.register(cellNib, forCellReuseIdentifier: "postCell")
    view.addSubview(commentsTable)
    commentsTable.register(LoadingCell.self, forCellReuseIdentifier: "loadingCell")

    self.commentsTable.delegate = self
    mainText.isEditable = false
    titleText.isEditable = false
    commentsTable.register(cellNib, forCellReuseIdentifier: "postCell")
    view.addSubview(commentsTable)
    commentsTable.register(LoadingCell.self, forCellReuseIdentifier: "loadingCell")
    print(delegate!)
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return comments.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if indexPath.section == 0 {
        let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! CommentTableViewCell
        cell.set(comment: comments[indexPath.row])
        return cell
    } else {
        let cell = tableView.dequeueReusableCell(withIdentifier: "loadingCell", for: indexPath) as! LoadingCell
        cell.spinner.startAnimating()
        return cell
    }
}

func textViewDidChange(_ commentView: UITextView) {
    commentPlaceHolder.isHidden = !newCommentLabel.text.isEmpty
}
结合周杰伦的评论,我开始运行视图。我必须添加comments.removAll(),因此它不会在commentsTable中多次打印注释。但是,
func textViewDidChange
不再起作用。我不知道如何解决这个问题。我尝试调用该函数,但没有成功。可能代理更改影响了这一点?

在这一行
注释(id:childDataSnapshot.key,text:comments)
您正在传递注释(这是数组属性),但需要传递文本。要重新加载数据,请使用
commentsTable.reloadData()

对于
cellForRowAt
中的错误,请仔细检查配置单元格所需传递的内容,并检查在
set(comment

中传递的类型。此答案基于问题中的数据,然后进行后续评论

数据非规范化是NoSQL数据库中的标准做法,但在这种情况下,问题中的结构可能比需要的更复杂

问题是

给定一系列的帖子,每个帖子都有评论,你如何加载 要在表格视图中显示的每篇文章的注释

我将从一个提议的结构开始反向工作

posts
   post_0 //created with .childByAutoId
      creator_uid: "the uid of whoever created this post"
      post_title: "My post about posting"

comments
   post_0 //ties back to the key of the post in the posts node
      comment_0 //created with .childByAutoId
         comment_by_uid: "whoever created this comment"
         comment_text: "comment about this post"
      comment_1
         comment_by_uid: "whoever created this comment"
         comment_text: "comment about this post"
此结构将评论从其引用的帖子中分离出来。在“评论”节点中,每个节点的关键是来自“帖子”节点的帖子id。这允许在表视图中加载帖子,而无需大量开销。例如,如果您在detailView中显示评论,请加载特定帖子的所有评论

func loadComments(forPostId: String) {
    let ref = self.ref.child("comments")
    let thisPostRef = ref.child(forPostId)
    thisPostRef.observeSingleEvent(of: .value, with: { snapshot in

        let allComments = snapshot.children.allObjects as! [DataSnapshot]
        for commentSnap in allComments {
            let commenterUid = commentSnap.childSnapshot(forPath: "comment_by_uid").value as? String ?? "No uid"
            let commentText = commentSnap.childSnapshot(forPath: "comment_text").value as? String ?? "No comment"
            //create a commentClass object, update properties and add to dataSourceArray
            print(commenterUid, commentText)
        }
        //tableView reload
    })
}
请注意,posts节点和comments节点键是使用.childByAutoId()创建的

现在是工作流。假设用户正在创建一篇新文章,并输入了该文章的标题和其他信息。调用此命令可在Firebase中创建该文章

func createPost(withTitle: String, andCreatorUid: String) {
    let postsRef = self.ref.child("posts")
    let thisPost = postsRef.childByAutoId()
    let d = [
        "post_title": withTitle,
        "creator_uid": andCreatorUid
        ]
    thisPost.setValue(d)
}
func addComment(toPostId: String, andComment: String, commentByUid: String) {
    let commentsRef = self.ref.child("comments") //ref to the comments node
    let thisCommentRef = commentsRef.child(toPostId) //ref to a node with postId as key
    let commentToAddRef = thisCommentRef.childByAutoId() //each comment will have it's own key
    let d = [
        "comment_text": andComment,
        "comment_by_uid": commentByUid]
    commentToAddRef.setValue(d)
}
这里有一个棘手的问题-我要做的是让posts节点有一个观察者。当添加一个新的post时,我会收到该事件,创建一个包含关于post的信息的PostsClass对象,并将其添加到我的数据源数组中,然后刷新我的tableView。通过这样做,我还可以获得节点的密钥(该节点是使用.childByAutoId创建的)

另一个用户看到该帖子并希望对其发表评论,因此他们点击该帖子以输入评论。下面的代码将他们的评论存储在Firebase中

func createPost(withTitle: String, andCreatorUid: String) {
    let postsRef = self.ref.child("posts")
    let thisPost = postsRef.childByAutoId()
    let d = [
        "post_title": withTitle,
        "creator_uid": andCreatorUid
        ]
    thisPost.setValue(d)
}
func addComment(toPostId: String, andComment: String, commentByUid: String) {
    let commentsRef = self.ref.child("comments") //ref to the comments node
    let thisCommentRef = commentsRef.child(toPostId) //ref to a node with postId as key
    let commentToAddRef = thisCommentRef.childByAutoId() //each comment will have it's own key
    let d = [
        "comment_text": andComment,
        "comment_by_uid": commentByUid]
    commentToAddRef.setValue(d)
}
toPostId是从他们选择添加注释的PostClass对象获得的帖子的键

最后,为了特别回答这个问题,这里是一个特定帖子的评论加载

func loadComments(forPostId: String) {
    let ref = self.ref.child("comments")
    let thisPostRef = ref.child(forPostId)
    thisPostRef.observeSingleEvent(of: .value, with: { snapshot in

        let allComments = snapshot.children.allObjects as! [DataSnapshot]
        for commentSnap in allComments {
            let commenterUid = commentSnap.childSnapshot(forPath: "comment_by_uid").value as? String ?? "No uid"
            let commentText = commentSnap.childSnapshot(forPath: "comment_text").value as? String ?? "No comment"
            //create a commentClass object, update properties and add to dataSourceArray
            print(commenterUid, commentText)
        }
        //tableView reload
    })
}
注:

我有一个类var,ref,因此self.ref指向我的根firebase节点。您需要将其设置为指向您的根firebase节点


在这个答案中,我使用post_0和comment_0作为节点键名,因为它比-LhzJD3tPL0xcnUDMaOZ这样的键更容易阅读和理解,这就是.childByAutoId将在firebase中实际创建的内容。

您的主要问题是
让comment=comment(id:childDataSnapshot.key,text:comments)
您应该传递
文本
注释
我不确定如何调用每个注释文本,因为每个注释文本都与一个唯一的id关联,如“-Lht1KEhkJR7ZPTcDqn8”或“-Lht1M76Bu9kTXJDezPp”我删除了我的答案。除了错误之外,整个JSON解析无法工作。例如,JSON中根本没有
id
键。而且不清楚用户id与
-Lht…
字典键之间的关系。@RyanSchiller,在您的代码中,
let text=dict[“comments”]as是什么?[String:Any]
part有吗?我想这是您的文本。注意:我假设您分析的部分是正确的。在代码中,将
let comment=comment(id:childDataSnapshot.key,text:comments)
替换为
let comment=comment(id:childDataSnapshot.key,text:text)
。应该可以。我不知道如何调用每个注释文本,因为每个注释文本都与一个唯一的id关联,如“-Lht1KEhkJR7ZPTcDqn8”或“-Lht1M76Bu9kTXJDezPp”创建类似于[uniqueId:comment text]的字典,然后按键搜索。类似于set的东西(text:commentsdicronary[uniqueId])我想这需要重新构造我的JSON我仍然不知道如何提取文本字符串本身而不是作为类型的注释。数据结构看起来不错。我用底部的当前代码更新了帖子。tableView仍然不会加载注释。现在,当我键入newCommentLabel textView时,我的注释占位符不会消失。有问题吗我正在使用数据源或加载视图?我的调试尝试尚未产生任何线索。@RyanSchiller如果您不打印
(commenterUid,commentText
)在for循环中的行中,是否打印每个注释的注释?@ryanscliller您确定注释数组为空吗?在我的最后一段代码中,我有
//创建一个commentClass
,您应该创建一个comment类,然后
self.commentArray.insert…
。然后跟随for循环(不在其中)应该重新加载tableView。除此之外,请确保tableView设置了其数据源和委托。您可能需要连接一个测试按钮,在运行loadComments函数后可以单击该按钮打印出数组的内容。该按钮不应为空。@RyansCollinger您的问题中有很多不相关的代码,我知道我的答案中的代码在我从现有项目中提取时起作用。我们目前并不真正关心表;如果数据源数组为空,那么tableView也会如此。您通常应该使用self引用类变量,并且在创建commentsClass对象时,它们可以直接添加到该数组中。因此
让aComment=C