I am using a Segmented controller to control how data is presented in my table view. One of the views should be chronologically which is easy because I use firebase so the query is effectively already chronological. However, I want to sort by highest rated which is a computed value (the "number of likes" is the count of a child which is an array of users who clicked "like". the table view controller instantiates an array:
var media = [Media]()
and a function fetches the media and ends like this:
let ref = DatabaseReference.media.reference()
let query = ref.queryOrdered(byChild: "uid").queryEqual(toValue: (value))
query.observe(.childAdded, with: { snapshot in
let media = Media(dictionary: snapshot.value as! [String : Any])
if !self.media.contains(media) {
self.media.insert(media, at: 0)
I think I need to make 2 different arrays and use a switch statement on segmented controller before the last 2 lines to populate them, then use switch statements in my TableView Data Source to read the associated arrays. Is this the right idea? If so, how do I sort snapshots?
Related
I have Tab Bar Controller, where I have few view controllers, but I want to pass array of values (workoutNames) to another view in my Tab Bar Controller. I wonder what's best option to do this and I've decided to use way of passing data with property. But when I try to retrieve data I get empty array. I could also use firestore to retrieve data in my Second View Controller, but It lasts too long, so I decided to passing data between views than retrieve data from firestore every time.
First View Controller
class HomeTableViewController: UIViewController
// I need to pass this array to another view
var workoutsName: [String] = []
...
func sendDataToCalendar() {
// IN THIS FUNCTION I RETRIEVE DATA FROM FIRESTORE AND UPDATE ARRAY WITH VALUES
// After all I print this array to check if everything is correct, and my data is here
print("\(workoutsName)")
}
Here is my Second View Controller when I want to use array from First View Controller
class CalendarViewController: UIViewController {
var arrayOfTitles = [String]()
.
.
.
func getArrayFromHome() {
let homeVC = HomeTableViewController()
homeVC.workoutsName = arrayOfTitles
// NOW I PRINT TO CHECK IF DATA EXISTS
print("\(arrayofTitles)"
}
And its empty, so data didn't pass.
Maybe it's not the best way to pass data, but main idea of this is that I will need to use this array in few view controllers. These controllers won't be connected by segue, so I can't use prepareforSegue. It's not one to one relationship (in future I will need this array in few controllers), so I shouldn't use delegate. I don't think that notification will be ok too, so I think that it's best option to pass data by property. But maybe I am wrong, so please correct me.
The reason why it doesn't work is that you instantiate a new HomeTableViewController with empty data.
If this data will be used on lots of place, why not save it locally? user default seems like it fit your needs.
func sendDataToCalendar() {
UserDefaults.standard.set(workoutsName, forKey: "workoutsName")
}
Then you can read it later on
func getWorkoutNameArray() {
let workoutName = UserDefaults.standard.object(forKey: "workoutsName") as? [String]
}
In your getArrayFromHome() function you are not accessing HomeTableViewController instance where you got the data but creating a new instance. That's why the array is empty. You end up with 2 instances of HomeTableViewController, one in use with the correct array and the dummy one created in the function with an empty array.
Would be better if you pass the data in the same place where you have a reference to CalendarViewController.
Let's say that you are creating and presenting CalendarViewController in your HomeTableViewController like:
let calendarViewController = CalendarViewController()
calendarViewController.arrayOfTitles = workoutNames
// Here is the presentation or push of calendarViewController
It will be useful for you to read this SO question
I have a collection view with a list of matches cell and a button to add new matches.
I'd like to have the same numbers of cell as per matches (IE: 10 matches, 10 cells) . I want the list every time the view appears.
var countOfMatches = [DataSnapshot]()
viewWillAppear {
ref.child("matches").observe(.childAdded, with: { (snapshot) in
self.countOfMatches = [DataSnapshot]()
for item in snapshot.children {
self.countOfMatches.append(item as! DataSnapshot)
}
})}
and in the collection view
return countOfMatches.count
Why my cells are not showing? I can see the results created under /matches in Firebase DB but not the cells
You should use .value instead of .childAdded.
Also you should reload the tableView.reloadData() method.
I am posting some information to Firebase database
let bookObject: Dictionary<String, Any> = [
"uid": uid,
]
FIRDatabase.database().reference().child("posts").child(key).setValue(bookObject)
and I want to retreive the data in a tableView
func loadData(){
if (FIRAuth.auth()?.currentUser) != nil{
FIRDatabase.database().reference().child("posts").child(self.loggedInUser!.uid).observeSingleEvent(of: .value, with: { (snapshot:FIRDataSnapshot) in
let loggedInUserData = snapshot
if let postsDictionary = snapshot .value as? [String: AnyObject] {
for post in postsDictionary {
self.posts.add(post.value)
}
self.TableView.reloadData()
}})}
}
Which works, however, the posts get loaded to the TableView randomly, sometimes a new post would be posted to the top of the TableView, sometimes it would be posted to the bottom of the TableView. What do I do so that the new post shows on top of the previous cell.
You'll want to add a timestamp to each post and then sort the posts based on the timestamp. You didn't post how your TableView is built, but I recommend using a tableArray made of multiple posts that are a custom class Post that has all the properties of the post in Firebase. Then you can arrange the posts by doing tableArray.sort({$0.date > $1.date}), where date is one of the variables in Post containing a String of the timestamp when it was created. You probably want to sort the tableArray right before where you reload the TableView's data.
You can then populate your TableView with
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = //dequeueResuableCell
//retrieve data from the cell's corresponding post
let postCreator = tableArray[indexPath.row].creatorUID
let postContent = tableArray[indexPath.row].content
...
//populate the cell using the above data
...
}
and since your posts in the tableArray are in chronological order, the posts in your TableView will also be in chronological order.
Have a look at the section called 'Sort data' on this page of Firebase's documentation.
https://firebase.google.com/docs/database/ios/lists-of-data
It basically allows you to order the result of your query by a child of your choice. In your case you should order by 'date' or 'timestamp' depending on what you store on your Firebase Real-Time database.
Example:
// My top posts by number of stars
let myTopPostsQuery = (ref.child("user-posts").child(getUid())).queryOrdered(byChild: "starCount")
If when you order by child you still see the new entries coming at the bottom of your tableView, you can simply reverse your array to ensure the newest will come at the top.
yourArray.reversed()
Hope this helps.
I have a table view with multiple selection enabled. Users are supposed to be able to select multiple options in a list and then change the sort order of the list. I would like to be able to keep the same cells selected, but the order of them will have changed from the sort. I'm not seeing a way to achieve this so any suggestions are appreciated.
If you have a "selected" indicator in your data structure, you could use it to rebuild the tableView selection.
If you can't add such an indicator to the data, you could perform your sort in two steps: 1) compute the new element indexes, 2) apply these indexes to your data array to perform the sort.
With the sort indexes, you can rebuild the selection based on the new location of the original indexes:
For example:
// Assuming your tableview is named "tableView",
// and the data array is named "dataArray",
// and you sort uses a member of dataArray called "sortKey"
// prepare list of new indexes for sorted data
let newIndexes = dataArray.enumerated().sorted{ $0.1.sortKey < $1.1.sortKey }.map{$0.0}
// determine new indexes of current selection
let newSelection = tableView.indexPathsForSelectedRows?
.map{IndexPath(row:newIndexes.index(of:$0.row)!, section:0)}
// sort and reload the data
dataArray = newIndexes.map{ self.dataArray[$0] }
tableView.reloadData()
// reapply the selection
tableView.indexPathsForSelectedRows?.forEach{tableView.deselectRow(at:$0, animated:false)}
for indexPath in newSelection ?? []
{ self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) }
I'm trying to develop my first app. This is kind of a restaurant and menu app. The first screen should be list of restaurants. This will list all the restaurants from Parse table. If one of the row(restaurant) is selected, it should segue to another view controller, but with chosen(restaurant's/personalized) menu. If any of the menu item is selected, then a third vc should open up with that menu details.
I've created story board design and segues, but not sure:
1) how to present restaurants from parse table into 1st VC ?
2) If selected, how to pass the resta.id from 1st vc to menuVc.
I've tried getting the table data from Parse to an array, but lost in how to pass it to didSelectRow in 1st VC and then to other VC respectively. Is there a way of taking the parse table data into rows for the restaurants table ?
var myRestaurant: [String] = [String]()
//Getting the data from the PFQuery class
var query = PFQuery(className: "Restaurants")
query.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
self.myRestaurant.append(object.objectForKey("ClassNameObject") as! String)
}
}
} else {
println("errorin fetching restaurant block")
}
}
Great to hear you're getting started with app development!
This is a very common scenario and will be a breeze to accomplish with the right tools. You can build out your own functionality for Parse within a TableViewController or you can use Parse's own PFQueryTableViewController to accomplish exactly what you want very easily.
Check out a tutorial to help get you started
Here's another just in case
The gist of it is, you must query Parse for data to fill the TableViewController's data source. With PFQueryTableViewController, all you have to do is specify a query in the function queryForTable(). PFQueryTableViewController is included in the ParseUI framework (along with a bunch of other great tools) which you will want to import.