Reverse order from query.whereKey("column", nearGeoPoint) in UITableView - uitableview

I'm trying to get location data(PFGeoPoint) from parse.com, show it in UITableView, and sort it by nearest one from user location.
I already use the code same with shown in parse documentation :
findPlaceData.whereKey("position", nearGeoPoint:SearchLocationGeoPoint)
I managed to get the data. I also managed to show it in my UITableView. The problem is, the order is reversed. I got the farthest in my first cell. Could anyone explain why this happen, and how to fix it?
import UIKit
class SearchTableViewController: UITableViewController {
#IBOutlet var SearchTitle: UILabel!
var userLocationToPass:CLLocation!
var categoryToPass:String!
var categoryIdToPass:String!
var placeData:NSMutableArray! = NSMutableArray()
override init(style: UITableViewStyle) {
super.init(style: style)
// Custom initialization
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func loadData(){
placeData.removeAllObjects()
let searchLocationGeoPoint = PFGeoPoint(location: userLocationToPass)
var findPlaceData:PFQuery = PFQuery(className: "Places")
findPlaceData.whereKey("category", equalTo: categoryIdToPass)
findPlaceData.whereKey("position", nearGeoPoint:searchLocationGeoPoint)
findPlaceData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!)->Void in
if error == nil{
for object in objects{
let place:PFObject = object as PFObject
self.placeData.addObject(place)
}
let array:NSArray = self.placeData.reverseObjectEnumerator().allObjects
self.placeData = NSMutableArray(array: array)
self.tableView.reloadData()
}
}
}
override func viewWillAppear(animated: Bool) {
loadData()
}
override func viewDidLoad() {
super.viewDidLoad()
SearchTitle.text = categoryToPass
println(userLocationToPass)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return placeData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:SearchTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as SearchTableViewCell
let place:PFObject = self.placeData.objectAtIndex(indexPath.row) as PFObject
cell.placeName.text = place.objectForKey("name") as? String
cell.placeOpenHour.text = place.objectForKey("openhour") as? String
return cell
}
}

Are you intentionally using the reverseObjectEnumerator? Because that could account for your results being reversed... The clue is in the method name ;-)
If you drop the following two lines from your code, it might not be reversed anymore.
let array:NSArray = self.placeData.reverseObjectEnumerator().allObjects
self.placeData = NSMutableArray(array: array)

Related

Gets number of rows but doesn't print

I have a program written in Swift 3, that grabs JSON from a REST api and appends it to a table view.
Right now, I'm having troubles with getting it to print in my Tableview, but it does however understand my count function.
So, I guess my data is here, but it just doesn't return them correctly:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, HomeModelProtocal {
#IBOutlet weak var listTableView: UITableView!
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
var feedItems: NSArray = NSArray()
var selectedLocation : Parsexml = Parsexml()
override func viewDidLoad() {
super.viewDidLoad()
self.listTableView.delegate = self
self.listTableView.dataSource = self
let homeModel = HomeModel()
homeModel.delegate = self
homeModel.downloadItems()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "BasicCell"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: Parsexml = feedItems[indexPath.row] as! Parsexml
myCell.textLabel!.text = item.title
return myCell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return feedItems.count
}
override func viewDidAppear(_ animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Are you by any chance able to see the error that I can't see?
Note. I have not added any textlabel to the tablerow, but I guess that there shouldn't be added one, when its custom?
Try this code:
override func viewDidLoad() {
super.viewDidLoad()
print(yourArrayName.count) // in your case it should be like this print(feedItems.count)
}

How to update the data from the searchResultsController (UISearchController)

So I am using a searchResultsController, which takes an array of Strings, and shows them in a tableview (It's an autocomplete list). When the user presses the 'Search' button on the keyboard, and the entered String is not yet in my Tableview, I want to add it, and update the tableview accordingly.
The issue is that once I added a String to the array, and make a new search, the array isn't updated with the new value!
Here is my code:
In my ViewDidLoad() on the Overview.swift class
class Overview: UIViewController,UISearchControllerDelegate,UISearchBarDelegate,UICollectionViewDelegate,UICollectionViewDataSource {
var mySearchController : UISearchController!
var mySearchBar : UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
let src = SearchResultsController(data: convertObjectsToArray())
// instantiate a search controller and keep it alive
mySearchController = UISearchController(searchResultsController: src)
mySearchController.searchResultsUpdater = src
mySearchBar = mySearchController.searchBar
//set delegates
mySearchBar.delegate = self
mySearchController.delegate = self
}
This is the data function, used for the UISearchController
func convertObjectsToArray() -> [String] {
//open realm and map al the objects
let realm = try! Realm()
let getAutoCompleteItems = realm.objects(AutoComplete).map({$0})
...
return convertArrayStrings // returns [String] with all words
}
So when the user pressed the search button on the keyboard, I save that word to my database.
Now I need to put the updated version of convertObjectsToArray() in my searchResultsController, but I haven't found out how to do this. All help is welcome
And last, but not least, my SearchResultsController class, which is used in the viewDidLoad of my Overview.swift class.
class SearchResultsController : UITableViewController {
var originalData : [String]
var filteredData = [String]()
init(data:[String]) {
self.originalData = data
super.init(nibName: nil, bundle: nil)
}
required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.filteredData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel!.text = self.filteredData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
clickedInfo = filteredData[indexPath.row]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
}
For the filtering of my words in the tableview (when user types something, only matching Strings are shown), I use the following extension.
extension SearchResultsController : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
let sb = searchController.searchBar
let target = sb.text!
self.filteredData = self.originalData.filter {
s in
let options = NSStringCompareOptions.CaseInsensitiveSearch
let found = s.rangeOfString(target, options: options)
return (found != nil)
}
self.tableView.reloadData()
}
You can use the search controller's update function for that I think:
func updateSearchResultsForSearchController(searchController: UISearchController) {
convertObjectsToArray()
self.tableView.reloadData()
}

SWIFT: Difficultly displaying data in tableView

I am attempting to display data from Parse onto the following tableView controller. For some reason, the data is not displaying on the tableView (i.e. the rows are blank). I do not think that the data queried from Parse is being appended to the arrays. I am wondering what I'm doing wrong here.
Here's the current output:
I am using a custom prototype cell with identifier "CellTrack" class "TrackTableViewCell" and as shown below:
Here is my code in the TableViewController file:
import UIKit
import Parse
class MusicPlaylistTableViewController: UITableViewController {
var usernames = [String]()
var songs = [String]()
var dates = [String]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
var query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
self.dates.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
print("added username")
let track = object["song"] as? String
self.songs.append(track!)
let date = object["createdAt"] as? String
self.dates.append(date!)
self.tableView.reloadData()
}
}
} else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.CreatedOn.text = dates[indexPath.row]
return cell
}
}
And here is my code in the "TrackTableViewCell.swift" class:
import UIKit
class TrackTableViewCell: UITableViewCell {
#IBOutlet weak var songTitle: UILabel!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var CreatedOn: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Execute your tableView.reloadData() in main thread.
dispatch_async(dispatch_get_main_queue(), {
self.tableViewCell.reloadData()
})
Try doing a guard let to see if those values are actually coming back as string or not. My guess would be that the value for created at never came back. Try it out and let me know.
guard let username = object["username"] as? String else {
print ("could not get username")
}
self.usernames.append(username)
print("added username")
guard let track = object["song"] as? String else {
print ("could not get song")
return
}
self.songs.append(track)
guard let date = object["createdAt"] as? String else {
print ("could not get createdAt")
return}
self.dates.append(date!)
func dequeueReusableCellWithIdentifier(_ identifier: String) -> UITableViewCell?
Return Value
A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
if cell == nil {
// create a new cell here
cell = TrackTableViewCell(...)
}
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.CreatedOn.text = dates[indexPath.row]
return cell
}

Retrieving User Profile Image and Username in TableView from Parse - Swift

I am able to fetch users and their profile pictures however, I'd like to return just the current user in the table and it doesn't seem possible since I'm using NSMutableArray to hold the user value.
UPDATE: I've edited to show my complete class
var userArray : NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
loadParseData()
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
// Do any additional setup after loading the view.
}
func loadParseData() {
var query : PFQuery = PFUser.query()!
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil {
if let objects = objects {
for object in objects {
self.userArray.addObject(object)
}
self.tableView.reloadData()
}
} else {
println("There is an error")
}
}
}
// Initialise the PFQueryTable tableview
override init(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//Configure PFQueryTableView
self.parseClassName = "User"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery {
var query = PFQuery(className: "User")
return query
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//How can I return PFUser.currentUser() here?
return self.userArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = indexPath.row
var userProfileImage = userArray[row] as! PFUser
var username = userProfileImage.username as String!
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! ProfileTableViewCell
//Display Profile Image
PFUser.currentUser()?["profile_picture"]
if let pfimage = PFUser.currentUser()?["profile_picture"] as? PFFile{
pfimage.getDataInBackgroundWithBlock({
(result, error) in
cell.profileImage.image = UIImage(data: result!)
cell.userName.text = username
})
}
return cell
}
You may declare your array as an AnyObject array. Try this:
var userArray : AnyObject = []
Try using some breakpoints to take a look on the variables and understand whats going on.
Also, if you have only one section on your Table View there is no need on calling numberOfSectionsInTableView since the defaul is 1.

Swift - Array index out of range warning

My code has an issue when I run the iOS simulator. It breaks and brings me to the line of code:let targetUser = users[indexPath.row] and says 'EXC_BAD_INSTRUCTION (CODE=EXC_1386_INVOP,snbcode = 0x0)' would anyone be able to help me figure out why?
class OverviewTableViewController: UITableViewController {
#IBOutlet weak var LogoutButton: UIBarButtonItem!
#IBOutlet weak var ChoosePartnerButton: UIBarButtonItem!
var rooms = [PFObject]()
var users = [PFUser]()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setLeftBarButtonItem(LogoutButton, animated: false)
self.navigationItem.setRightBarButtonItem(ChoosePartnerButton, animated: false)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if PFUser.currentUser() != nil {
loadData()
}
}
func loadData() {
rooms = [PFObject]()
users = [PFUser]()
self.tableView.reloadData()
let pred = NSPredicate(format: "user1 = %# OR user2 = %#", PFUser.currentUser()!, PFUser.currentUser()!)
let roomQuery = PFQuery(className: "Room", predicate: pred)
roomQuery.includeKey("user1")
roomQuery.includeKey("user2")
roomQuery.findObjectsInBackgroundWithBlock { (results:[AnyObject]?, error:NSError?) -> Void in
if error == nil {
self.rooms = results as! [PFObject]
for room in self.rooms {
let user1 = room.objectForKey("user1") as! PFUser
let user2 = room["user2"] as! PFUser
if user1.objectId != PFUser.currentUser()?.objectId {
self.users.append(user1)
}
if user2.objectId != PFUser.currentUser()?.objectId {
self.users.append(user2)
}
}
self.tableView.reloadData()
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return rooms.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 80
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! OverviewTableViewCell
let targetUser = users[indexPath.row]
cell.nameLabel.text = targetUser.username
return cell
}
Sounds like #MartinR had nailed it. You're reading rooms.count in numberOfRowsInSection, but then looking up data from the users array in cellForRowAtIndexPath.
You could figure this out in the debugger by examining indexPath.row when you crash, and examining the size of rooms.count as well.

Resources