在 UITableView 中按时间顺序排列 Firebase 帖子(最新的优先)

Posted

技术标签:

【中文标题】在 UITableView 中按时间顺序排列 Firebase 帖子(最新的优先)【英文标题】:Ordering Firebase posts chronologically in UITableView (newest first) 【发布时间】:2018-04-25 11:25:28 【问题描述】:

我正在尝试,并且已经有很长一段时间了,将我的帖子排序为 Firebase 中最新的。我知道 Firebase 在使用 childByAutoId() 创建时会自动执行此操作,但我认为这可能是我将数据调用到 UITableView 中的方式。

这是将数据加载到我的数组中的代码:

数组:

var posts = [[String : AnyObject]]()

函数'loadData()':

let uid = Auth.auth().currentUser?.uid
    Database.database().reference().child("userinfo").child(uid!).observeSingleEvent(of: .value, with:  (snapshot) in
        let value = snapshot.value as? NSDictionary
        let userFilter = value?["discover_filter"] as! String

        if userFilter == "__" 
            Database.database().reference().child("posts").queryLimited(toLast: 50).observeSingleEvent(of: .value, with:  (snapshot) in

                self.posts.removeAll()

                if let postsDictionary = snapshot.value as? [String: AnyObject] 
                    for post in postsDictionary 
                        self.posts.append(post.value as! [String : AnyObject])

                    
                    DispatchQueue.main.async 
                        self.postsTableView.reloadData()
                    
                
            )
        else 
            Database.database().reference().child("posts").queryOrdered(byChild: "combinedstring").queryEqual(toValue: userFilter).queryLimited(toLast: 50).observeSingleEvent(of: .value, with:  (snapshot) in

                self.posts.removeAll()

                if let postsDictionary = snapshot.value as? [String: AnyObject] 
                    for post in postsDictionary 
                        self.posts.append(post.value as! [String : AnyObject])
                    
                    DispatchQueue.main.async 
                        self.postsTableView.reloadData()
                    
                
            )
        
    )

我尝试自己修复它,但我无法弄清楚,我尝试了许多不同的方法,但得出的结论是我只需要帮助 - 我很新,只学习 Swift 大约现在 6 个月(我自己作为我学位项目的一部分)。

如果还有什么需要请告诉我。

提前谢谢你!

Firebase JSON 结构:

"posts" : 
"-LAxSTwPhGO9NStRFGK3" : 
  "activity" : "lol",
  "combinedstring" : "League of Legends_PC_Beginner",
  "communication" : "Mic",
  "console" : "PC",
  "description" : "Description...",
  "game" : "League of Legends",
  "gameimage" : "League of Legends.jpg",
  "key" : "-LAxSTwPhGO9NStRFGK3",
  "lfglfm" : "Looking for Group",
  "skill" : "Beginner",
  "timestamp" : 1524670787479,
  "uid" : "TgtlcIcd29gN2GEHLOO1QvGhAQA2",
  "username" : "danielrjjones"
,
"-LAxSbp8ilXUVY7yFG_C" : 
  "activity" : "lol",
  "combinedstring" : "Fifa 17_Xbox One_Beginner",
  "communication" : "Mic",
  "console" : "Xbox One",
  "description" : "Description...",
  "game" : "Fifa 17",
  "gameimage" : "Fifa 17.jpg",
  "key" : "-LAxSbp8ilXUVY7yFG_C",
  "lfglfm" : "Looking for Members",
  "skill" : "Beginner",
  "timestamp" : 1524670823883,
  "uid" : "TgtlcIcd29gN2GEHLOO1QvGhAQA2",
  "username" : "danielrjjones"


用于创建我的帖子的代码:

import UIKit
import Firebase
import FirebaseDatabase

class CreatePostViewController: UIViewController, UITextFieldDelegate, UIPickerViewDataSource, UIPickerViewDelegate 

@IBOutlet weak var gameTextField: UITextField!
@IBOutlet weak var activityTextField: UITextField!
@IBOutlet weak var consoleTextField: UITextField!
@IBOutlet weak var skillTextField: UITextField!
@IBOutlet weak var communicationTextField: UITextField!
@IBOutlet weak var lfglfmTextField: UITextField!
@IBOutlet weak var rulesTextView: UITextView!
@IBOutlet weak var descriptionTextView: UITextView!
@IBOutlet weak var createButton: UIButton!

var gameValue = String()

var console = ["Select Console", "Nintendo DS", "Nintendo Switch", "PC", "PS Vita", "Playstation 3", "Playstation 4", "Xbox 360", "Xbox One"]
var skill = ["Select Playing Type", "Beginner", "Experienced", "Veteran", "Pro"]
var communication = ["Select Communication","Mic", "No Mic"]
var lfglfm = ["Select LFG/LFM", "Looking for Group", "Looking for Members"]

var itemSelected = ""
var gameimage = ""
var combinedString = ""

weak var pickerView: UIPickerView?
weak var textField: UITextField?

override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    //allow tap on screen to remove text field input from screen
    self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:))))

    //UIPICKER
    let pickerView = UIPickerView()
    pickerView.delegate = self
    pickerView.dataSource = self

    gameTextField.delegate = self
    consoleTextField.delegate = self
    skillTextField.delegate = self
    communicationTextField.delegate = self
    lfglfmTextField.delegate = self

    //set pickerview for other text fields
    pickerView.showsSelectionIndicator = true

    let toolBar = UIToolbar()
    toolBar.barStyle = UIBarStyle.default
    toolBar.isTranslucent = true
    toolBar.tintColor = UIColor(red:0.00, green:0.45, blue:1.00, alpha:1.0)
    toolBar.sizeToFit()

    let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(CreatePostViewController.donePicker))
    let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
    let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: #selector(CreatePostViewController.donePicker))

    toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
    toolBar.isUserInteractionEnabled = true

    gameTextField.inputView = pickerView
    gameTextField.inputAccessoryView = toolBar
    consoleTextField.inputView = pickerView
    consoleTextField.inputAccessoryView = toolBar
    skillTextField.inputView = pickerView
    skillTextField.inputAccessoryView = toolBar
    communicationTextField.inputView = pickerView
    communicationTextField.inputAccessoryView = toolBar
    lfglfmTextField.inputView = pickerView
    lfglfmTextField.inputAccessoryView = toolBar

    // pickerview = pickerview
    self.pickerView = pickerView

    //extra styling
    let navImg = UIImage(named: "nav-bar")
    navigationController?.navigationBar.setBackgroundImage(navImg, for: .default)

    //createButton.colourBorder()



override func viewDidAppear(_ animated: Bool) 
    gameTextField.text = gameValue
    gameTextField.resignFirstResponder()


@objc func donePicker() 
    textField?.resignFirstResponder()
    self.view.endEditing(true)


//pickerview code
func textFieldDidBeginEditing(_ textField: UITextField) 
    if textField == gameTextField 
        return
    else 
        self.pickerView?.reloadAllComponents()
    


func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
    if consoleTextField.isFirstResponder
        return console.count
    else if skillTextField.isFirstResponder
        return skill.count
    else if communicationTextField.isFirstResponder
        return communication.count
    else if lfglfmTextField.isFirstResponder
        return lfglfm.count
    
    return 0


func numberOfComponents(in pickerView: UIPickerView) -> Int 
    return 1


func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? 
    if consoleTextField.isFirstResponder
        return console[row]
    else if skillTextField.isFirstResponder
        return skill[row]
    else if communicationTextField.isFirstResponder
        return communication[row]
    else if lfglfmTextField.isFirstResponder
        return lfglfm[row]
    
    return nil


func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
    if consoleTextField.isFirstResponder
        let itemselected = console[row]
        consoleTextField.text = itemselected
    else if skillTextField.isFirstResponder
        let itemselected = skill[row]
        skillTextField.text = itemselected
    else if communicationTextField.isFirstResponder
        let itemselected = communication[row]
        communicationTextField.text = itemselected
    else if lfglfmTextField.isFirstResponder
        let itemselected = lfglfm[row]
        lfglfmTextField.text = itemselected
    


@IBAction func createPostTapped(_ sender: UIButton) 

    if gameTextField.text == "" || consoleTextField.text == "" || skillTextField.text == "" || communicationTextField.text == "" || lfglfmTextField.text == "" 
        let alert = UIAlertController(title: "Oops...", message: "You must fill out all fields to create a post. Please try again!", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
     else if consoleTextField.text == "Select Console" || skillTextField.text == "Select Playing Type" || communicationTextField.text == "Select Communication" || lfglfmTextField.text == "Select LFG/LFM" 
        let alert = UIAlertController(title: "Oh no!", message: "Please select a value to create a post.", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
     else 

        if let uid = Auth.auth().currentUser?.uid 
            Database.database().reference().child("usernames").child(uid).observeSingleEvent(of: .value, with: 
                (snapshot) in
                if let userDictionary = snapshot.value as? [String: AnyObject] 
                    for user in userDictionary 
                        if let username = user.value as? String 
                            if let game = self.gameTextField.text 
                                if let activity = self.activityTextField.text 
                                    if let console = self.consoleTextField.text 
                                        if let skill = self.skillTextField.text 
                                            if let communication = self.communicationTextField.text 
                                                if let lfglfm = self.lfglfmTextField.text 
                                                    if let description = self.descriptionTextView.text 

                                                        let gameimage = "\(self.gameTextField.text!).jpg"
                                                        let combinedString = "\(self.gameTextField.text!)_\(self.consoleTextField.text as! String)_\(self.skillTextField.text as! String)"

                                                        let timeStamp = ServerValue.timestamp() as! [String:Any]

                                                        let refer = Database.database().reference().child("posts").childByAutoId()
                                                        let ref = refer.key

                                                        let postObject: Dictionary<String, Any> = [
                                                            "uid" : uid,
                                                            "username" : username,
                                                            "game" : game,
                                                            "activity" : activity,
                                                            "console" : console,
                                                            "skill" : skill,
                                                            "communication" : communication,
                                                            "lfglfm" : lfglfm,
                                                            "description" : description,
                                                            "gameimage" : gameimage,
                                                            "combinedstring" : combinedString,
                                                            "timestamp" : timeStamp,
                                                            "key" : ref,
                                                            ]

                                                        refer.setValue(postObject)

                                                        //Database.database().reference().child("posts").childByAutoId().setValue(postObject)

                                                        let alert = UIAlertController(title: "Success!", message: "Your post was added successfully.", preferredStyle: .alert)
                                                        alert.addAction(UIAlertAction(title: "OK", style: .default, handler:  (action) in
                                                            //code will run when ok button is pressed
                                                            let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoggedInVC")
                                                            self.present(vc!, animated: true, completion: nil)
                                                        ))
                                                        self.present(alert, animated: true, completion: nil)

                                                    
                                                
                                            
                                        
                                    
                                
                            
                        
                    
                
            )
        
    


【问题讨论】:

您可以添加一个类型为 number 的特定字段,例如 sortNbr 并将您计算的时间戳(例如纪元时间)分配为值,然后构建一个按此字段值排序的查询,例如:let docRef = db.collection("xxxx") docRef.order(by: "sortNbr") 我不知道你的意思是什么,你能详细说明一下吗? 【参考方案1】:

这应该可行:

Database.database().reference().child("posts").queryOrderedByKey().queryLimited(toLast: 50).observeSingleEvent(of: .value, with:  (snapshot: DataSnapshot) in
    self.posts.removeAll()
    for child in snapshot.children.allObjects as [DataSnapshot] 
        self.posts.append(child.value as? [String: AnyObject])
    

    DispatchQueue.main.async 
        self.postsTableView.reloadData()
    
)

变化:

    显式调用queryOrderedByKey() 以明确我们希望按键排序的结果。 在闭包中循环 snapshot.children,因为过早转换为 [String: AnyObject] 会丢失订购信息。

【讨论】:

使用你的编辑我得到错误:'类型'Any'的值没有成员'值''你知道我为什么会得到这个吗? 啊,可能还没有输入。我更新了,但是看看这些:google.com/… 谢谢,这现在确实显示了帖子,但不幸的是没有按最新顺序显示帖子。我可以提供更多信息来提供帮助吗? 我在帖子中包含了我的 JSON 结构。 Firebase 中的排序始终是升序的。如果要反转它们,则必须在客户端执行该操作,或者添加具有反转值的属性。有关更多信息,请参阅***.com/questions/45137899/…、***.com/questions/45419057/…【参考方案2】:

(根据答案中的 cmets)一种解决方案是为您的每个“帖子”添加一个类型为数字的特定字段(例如称为timestamp)并分配创建时间戳乘以 - 1

时间戳的推荐值是纪元时间,请参阅Get Unix Epoch Time in Swift。

然后您根据此字段值对查询进行排序,例如:

Database.database().reference().child("posts").queryOrdered(byChild: "timestamp")...

这样,最新的帖子会最先出现

【讨论】:

我不太确定如何实现这个,之前有人推荐过,但我还没弄清楚如何。 @DanielJones 您如何创建帖子?您可以使用用于创建问题的代码来编辑您的问题吗? @DanielJones 实际上你已经在存储一个时间戳了!您确实让 timeStamp = ServerValue.timestamp() 为! [String:Any] 并在 let postObject: Dictionary = [] 中添加时间戳。所以你已经可以尝试查询这个值的 ordered 了。但我认为你想要它以相反的时间顺序,不是吗? (见下一条评论) 因此您应该找到一种方法来存储此时间戳*(-1)。我认为不可能直接将 ServerValue.timestamp() 相乘,因此您必须在代码中生成时间戳并在那里进行数学运算。现在,没有任何阿谀奉承,我建议你试试弗兰克的解决方案,我相信它比我提议的要好! 我已经尝试过 Franks 解决方案,但它仍然无法正常工作 - 它只是以与之前相同的方式获取数据

以上是关于在 UITableView 中按时间顺序排列 Firebase 帖子(最新的优先)的主要内容,如果未能解决你的问题,请参考以下文章

NestJS 在 SwaggerUI 中按字母顺序排列端点

dplyr:在 R 中按字母顺序排列列

vis.js 时间线视图中按时间顺序排列的项目集

在 Django 模型查询中按顺序排列的原始 SQL

PHP 在Wordpress中按字母顺序排列的相邻帖子

如何更新账单编号栏?或在 sql server 的列中按顺序排列