For the past few days, I have been trying to create an Instagram-like feed for my app. To be more specific: load new posts (5) each time the user updates the feed from the bottom.
I am currently using Firebase to both store and display my data.
My code so far, looks like this:
var ref:FIRDatabaseReference!
var dict = [String:Any]()
var posts = [[String:Any]]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
ref = FIRDatabase.database().reference()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
loadValues()
}
func loadValues() {
dict.removeAll()
posts.removeAll()
ref.child("posts").queryOrderedByChild("timeCreated").queryLimitedToLast(5).observeEventType(.ChildAdded) { (snapshot:FIRDataSnapshot) in
if let timeCreated = snapshot.value!["timeCreated"] as? Int {
self.dict["timeCreated"] = timeCreated
}
if let postText = snapshot.value!["postText"] as? String {
self.dict["postText"] = postText
}
self.posts.append(self.dict)
self.tableView.reloadData()
}
}
func scrollViewDidScroll(scrollView: UIScrollView) {
if (scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height {
//tableView.tableFooterView!.hidden = true
let pagingSpinner = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
pagingSpinner.startAnimating()
pagingSpinner.hidesWhenStopped = true
pagingSpinner.sizeToFit()
tableView.tableFooterView = pagingSpinner
//loadMore(5)
} else {
let pagingSpinner = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
pagingSpinner.stopAnimating()
pagingSpinner.hidesWhenStopped = true
pagingSpinner.sizeToFit()
pagingSpinner.hidden = true
tableView.tableFooterView = pagingSpinner
tableView.tableFooterView?.hidden = true
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
if let postText = posts[indexPath.row]["postText"] as? String {
cell.textLabel!.text = postText
}
return cell
}
func loadMore(increment:Int) {
//What should go in here?
}
So what I am trying to do here - is that I am detecting when the user has scrolled to the bottom (in my scrollViewDidScroll function. Then I am sinply displaying the activity indicator, and calling the function loadMore(5) where 5 is the amount of new posts that I want to display.
So here I have two problems. The timeCreated variable is simply a timestamp, where I have ten records (1-10, where 10 is the newest, and 1 is the oldest). With this code that I have now, the tableView displays the data in an ascending view, starting at 5 and ending at 10.
I have tried to reverse the array of dictionaries (post) by simply doing a .reverse() before appending the dict in the loadValues function. as I simply want it to display 10 at the top, and 5 at the bottom.
The second problem I have is that I can't really seem to find a good and effective way of updating the tableView (adding another 5 records). I have tried to simply just have a global variable with the default value of 5, and then on loadMore simply plus it by five, and then do a removeAll() on both the dict and posts - with no luck (the tableView scrolls to the top, which I don't want to). I have also tried to play with both queryLimitedTolast and queryLimitedToFirst where I ended up duplicating some data.
So in other words, I also need to check that the user can in fact load 5 new unique posts (or ex. 3, if there's only 3 unique posts left).
Does anyone have any ideas on how I would approach this?
Help would be greatly appreciated, as I have struggled with this for the past two days now.
If you are using tableView
update your DataSource instead of adding a row at a particular index. using struct
is a common aproach.
struct dataS {
var postData : String!
var index_Initial : Int!
init(post : String!, ind : Int!)
{
self.postData = post
self.index_Initial = ind
}
}
Declare an array of type
dataSourceS
var dataFeed= [dataS]()
For knowing that how many posts you have already retrived , you need to keep the index of each post in the the
post
node itself.Which can be done by counting the no of children in thepost
node and incrementing it by one.Or creating a complete separate node ofnoOfPosts: 100, //Lets say there are 100 posts in your DB Posts : { Post1:{ text : asdasdasd, index : 12 }, Post2:{ text : asdasddasdasd, index : 13 },..... }
Your final code will look something like this:-
import UIKit
import Firebase
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var dataFeed = [dataS]()
let pagingSpinner = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
var totalNoOfPost : Int!
@IBOutlet weak var customTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
customTableView.delegate = self
customTableView.dataSource = self
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
FIRDatabase.database().reference().child("Posts").observeSingleEventOfType(.Value, withBlock: {(snap) in
if let postDict = snap.value as? [String:AnyObject]{
self.totalNoOfPost = postDict.count
self.loadMore()
}
})
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataFeed.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = customTableView.dequeueReusableCellWithIdentifier("customCell") as! customTableViewCell
if dataFeed.count > 0{
cell.poatLabel.text = dataFeed[indexPath.row].postData
}
return cell
}
func loadMore(){
let initialFeedCount : Int = dataFeed.count
if totalNoOfPost - initialFeedCount - 4 > 0{
FIRDatabase.database().reference().child("Posts").queryOrderedByChild("index").queryStartingAtValue(totalNoOfPost - initialFeedCount - 4).queryEndingAtValue(totalNoOfPost - initialFeedCount).observeEventType(.Value, withBlock: {(recievedSnap) in
if recievedSnap.exists(){
for each in recievedSnap.value as! [String:AnyObject]{
let temp = dataS.init(post: each.1["text"] as! String, ind : each.1["index"] as! Int)
self.dataFeed.insert(temp, atIndex: 5 * Int(self.dataFeed.count/5))
self.dataFeed.sortInPlace({$0.index_Initial > $1.index_Initial})
if self.dataFeed.count == initialFeedCount+5{
self.dataFeed.sortInPlace({$0.index_Initial > $1.index_Initial})
self.customTableView.reloadData()
}
}
}
}, withCancelBlock: {(err) in
print(err.localizedDescription)
})
}else if totalNoOfPost - initialFeedCount - 4 <= 0{
FIRDatabase.database().reference().child("Posts").queryOrderedByChild("index").queryStartingAtValue(0).queryEndingAtValue(totalNoOfPost - initialFeedCount).observeEventType(.Value, withBlock: {(recievedSnap) in
if recievedSnap.exists(){
for each in recievedSnap.value as! [String:AnyObject]{
let temp = dataS.init(post: each.1["text"] as! String, ind : each.1["index"] as! Int)
self.dataFeed.insert(temp, atIndex: 5 * Int(self.dataFeed.count/5))
self.dataFeed.sortInPlace({$0.index_Initial > $1.index_Initial})
if self.dataFeed.count == initialFeedCount+4{
self.dataFeed.sortInPlace({$0.index_Initial > $1.index_Initial})
self.customTableView.reloadData()
self.pagingSpinner.stopAnimating()
}
}
}else{
self.pagingSpinner.stopAnimating()
}
}, withCancelBlock: {(err) in
print(err.localizedDescription)
})
}
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if (indexPath.row + 1) == dataFeed.count {
print("Displayed the last row!")
pagingSpinner.startAnimating()
pagingSpinner.hidesWhenStopped = true
pagingSpinner.sizeToFit()
customTableView.tableFooterView = pagingSpinner
loadMore()
}
}
}
struct dataS {
var postData : String!
var index_Initial : Int!
init(post : String!, ind : Int!)
{
self.postData = post
self.index_Initial = ind
}
}
来源:https://stackoverflow.com/questions/39430628/retrieve-only-5-users-at-a-time-firebase-like-instagram