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