No index path for table cell being reused in Swift - ios

I noticed there are many questions on this in Obj-C but I hardly remember Obj-C and each of the answers was specific to the question. Here I get this error: "No index path for table cell being reused" sometimes when the app refreshes. I notice that when I don't refresh but I leave and reopen the table view the formatting is ruined.
Here is my "refresh" method used in a few places:
#IBAction func loadData(){
timeLineData.removeAllObjects()
//pulls the data from the server
var findTimeLineData: PFQuery = PFQuery(className: "Sweets")
findTimeLineData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error: NSError!) -> Void in
if !error{
for object:PFObject! in objects{
self.timeLineData.addObject(object)
}
let tempArray: NSArray = self.timeLineData.reverseObjectEnumerator().allObjects
self.timeLineData = tempArray as NSMutableArray
//reloads the data in the table view
self.tableView.reloadData()
}
}
}
And the tableview method:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {
let cell: SweetTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as SweetTableViewCell
let sweet: PFObject = self.timeLineData.objectAtIndex(indexPath.row) as PFObject
//part of the animation
cell.sweetTextView.alpha = 0
cell.userNameLabel.alpha = 0
cell.timestampLabel.alpha = 0
cell.sweetTextView.text = sweet.objectForKey("content") as String
//add the date
var dateFormatter: NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm yyyy-MM-dd"
cell.timestampLabel.text = dateFormatter.stringFromDate(sweet.createdAt)
//finds the sweeter associated with a pointer
var findSweeter: PFQuery = PFUser.query()
findSweeter.whereKey("objectId", equalTo: sweet.objectForKey("sweeter").objectId)
findSweeter.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]!, error: NSError!)-> Void in
if !error{
let user: PFUser = (objects as NSArray).lastObject as PFUser
cell.userNameLabel.text = user.username
}
}
//adds animation
UIView.animateWithDuration(1, animations: {
cell.sweetTextView.alpha = 1
cell.userNameLabel.alpha = 1
cell.timestampLabel.alpha = 1
})
return cell
}
Any idea what is causing the error?

What's causing the problem for you: when you scroll your table, the TableView dequeues the cell that your async call to parse server is trying to manipulate.
You can overcome this problem by:
1- in your Sweets table on Parse, store PFUser object as a pointer
2- in your loadData function, fetch user from the query by includeKey method of PFQuery
If you change according to this, you won't have to query the PFUser every time the cellForRowAtIndexPath is called.

Related

Counting found objects in PFTableQueryViewController

I am trying to count the number of the found objects in PFQueryTableViewController.
I have tried working around with
override func queryForTable() -> PFQuery {
let query = PFQuery(className: self.parseClassName!)
query.whereKey("member", equalTo: memberId!)
let count = query.countObjectsInBackground()
label.text = "\(count)"
return query
}
But my app will crash.
EDIT:
The issue is not to make a query and count it's objects. The problem is to use queryForTable passing my query to cellForRowAtIndexPath of my PFQueryTableViewController
the cellForRowAtIndexPath looks like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell:DetailApplicantCell = self.table.dequeueReusableCellWithIdentifier("reuseIdentifier") as! DetailApplicantCell
if let name = object?.objectForKey(self.textKey!) as? String{
cell.nameLbl.text = name
}
cell.groupImage.image = UIImage(named: "People.png")
if let imageFile = object?.objectForKey(self.imageKey!) as? PFFile{
cell.groupImage.file = imageFile
cell.groupImage.loadInBackground()
}
return cell
}
NOTE that this is not the default cellForRow
Try with query.findObjectsInBackgroundWithBlock method and get the size() of the response object
let query = PFQuery(className: self.parseClassName!)
query.whereKey("member", equalTo: memberId!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
let count = objects.size()
label.text = "\(count)"
if let object = objects as? [PFObject] {
}
} else {
// Log details of the failure
print("Error: \(error!)")
}
}
You are force unwrapping at 2 places, use if let:
func queryForTable() -> PFQuery? {
if let parseClass = self.parseClassName {
let query = PFQuery(className: parseClass)
if let id = memberId {
query.whereKey("member", equalTo: id)
}
let count = query.countObjectsInBackground()
label.text = "\(count)"
return query
}
return nil
}
Then you use your function like:
if let query = queryForTable() {
//your query related code here.
}
Rather than doing a second PFQuery I found a better way using a method of PFQueryTableViewController like this:
override func objectsDidLoad(error: NSError?) {
super.objectsDidLoad(error)
print("objectsDidLoad")
if let results = self.objects{
print("objectsFound")
self.groupsCountLbl.text = "\(results.count)"
self.groupsCountLbl.fadeIn()
}
}
The VC has a property objects an array of AnyObject?.
With the objectsDidLoad function you determine the time, everything is loaded.

UITableview reloading tableview and cell

I am trying to reload my table view using
self.tableView.reloadData()
It works properly if I'm loading static datasource using array. Everything work properly.
But when I try to use my query function with parse, it loads a new cell but the contents of the tableview cell doesn't change. If I re-open the app, the cells will update properly.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "EmpPostTVCellIdentifier"
let cell: EmpPostTVCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? EmpPostTVCell
//If datasource
if dataSource.isEmpty{
fetchDataFromParse()
print("no posts")
}
let itemArr:PFObject = self.dataSource[indexPath.row]
cell?.companyPostLabel.text = (PFUser.currentUser()?.objectForKey("companyName")!.capitalizedString)! as String
cell?.occupationPostLabel.text = itemArr["occupation"]!.capitalizedString as String
cell?.countryPostLabel.text = itemArr["country"]!.capitalizedString as String
let companyImage: PFFile?
companyImage = PFUser.currentUser()?.objectForKey("profileImageEmployer") as? PFFile
companyImage?.getDataInBackgroundWithBlock({ (data, error) -> Void in
if error == nil{
cell?.companyLogoImage.image = UIImage(data: data!)
}
})
let dateArr = createdByDate[indexPath.row]
let strDate = Settings.dateFormatter(dateArr)
cell?.closingDateLabel .text = strDate
return cell!
}
I am using pull to refresh my tableviews contents using this code
func refresh(sender:AnyObject)
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.fetchDataFromParse()
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
})
}
with or without the dispatch_asynch function the results remains the same. It just add new tableviewcell but the contents in it does not change. Any ideas guys?
edit 1 :
func fetchDataFromParse() {
// MARK: - JOB POST QUERY
if PFUser.currentUser()?.objectId == nil{
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success, error) -> Void in
let query = PFQuery(className: "JobPost")
//creating a pointer
let userPointer = PFUser.objectWithoutDataWithObjectId(PFUser.currentUser()?.objectId)
query.whereKey("postedBy", equalTo: userPointer)
query.orderByDescending("createdAt")
let objects = query.findObjects()
for object in (objects as? [PFObject])!{
//print(object.objectId)
self.dataSource.append(object)
self.createdByDate.append((object.objectForKey("closingDate") as? NSDate)!)
print(self.dataSource)
print(self.createdByDate)
}
})
} else {
let query = PFQuery(className: "JobPost")
//creating a pointer
let userPointer = PFUser.objectWithoutDataWithObjectId(PFUser.currentUser()?.objectId)
query.whereKey("postedBy", equalTo: userPointer)
query.orderByDescending("createdAt")
let objects = query.findObjects()
for object in (objects as? [PFObject])!{
//print(object.objectId)
self.dataSource.append(object)
self.createdByDate.append((object.objectForKey("closingDate") as? NSDate)!)
print(self.dataSource)
print(self.createdByDate)
}
}//end of PFUser objectID == nil else clause
}
Let's see the content of the fetchDataFromParse() function where I presume you're filling the self.dataSource array
Try to call self.tableview.reloadData() when fetchDataFromParse() is finished.
Check whether your dataSource array is empty at the end of your fetchDataFromParse method
PFUser.currentUser()?.saveInBackgroundWithBlock is an asynchronus method. So your tableView cell is having no data.

Use of unresolved identifier 'objectAtIndexPath'

I am working on an app that allows you to 'Like' posts. I was implementing the like button, but I got an error that I cannot seem to fix.
I searched in another posts, but I'm unsure of how to fix it.
This is the code I'm using to implement the like button. Do I need to import something into my project? Or unwrap at certain point?
Any help is appreciate it.
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
let cell:PostTableViewCell = tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath!) as! PostTableViewCell
let post = self.objectAtIndexPath(indexPath!) as PFObject
cell.postTextView.alpha = 0
cell.usernameLabel.alpha = 0
cell.timestampLabel.alpha = 0
cell.postTextView.text = post.objectForKey("content") as! String
var dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "yyyy-MM-dd HH:mm"
cell.timestampLabel.text = dataFormatter.stringFromDate(post.createdAt!)
// to get username from the post
var showUsername:PFQuery = PFUser.query()!
//the objectID is the same as the user in the two different tables
showUsername.whereKey("objectId", equalTo: post.objectForKey("user")!.objectId!!)
showUsername.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil{
let user = (objects as! [PFUser]).last
cell.usernameLabel.text = user!.username
UIView.animateWithDuration(0.5, animations: {
cell.postTextView.alpha = 1
cell.usernameLabel.alpha = 1
cell.timestampLabel.alpha = 1
})
}
}
return cell
}
func objectAtIndexPath(indexPath: NSIndexPath) -> PFObject {
return self.timelineData[indexPath.row] as! PFObject
}
#IBAction func likeButton(sender: UIButton) {
//disables the like button so it can't be pressed again
sender.enabled = false
sender.userInteractionEnabled = false
sender.alpha = 0.5
//get the point in the table view that corresponds to the button that was pressed
//in my case these were a bunch of cells each with their own like button
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = self.objectAtIndexPath(hitIndex!) as PFObject
//this is where I incremented the key for the object
object.incrementKey("likes")
object.saveInBackground() //still gives me error here
self.tableView.reloadData()
NSLog("Top Index Path \(hitIndex?.row)")
}
Update2: Added a photo of the error
Since the view controller is neither an NSFetchedResultsController or a PFQueryTableViewController, you'll have to implement objectAtIndexPath: yourself.
A hint about the code you need is in cellForRowAtIndexPath':
let post:PFObject = self.timelineData.objectAtIndex(indexPath!.row) as! PFObject
Dispensing with the objectAtIndex method on array, just index into the array at the row:
func objectAtIndexPath(indexPath: NSIndexPath) -> PFObject {
return self.timelineData[indexPath.row] as! PFObject
}
Call it wherever the old code appears like this (in likeButton)...
let object = self.objectAtIndexPath(hitIndex) as! PFObject
or, in cellForRowAtIndexPath:...
let post = self.objectAtIndexPath(indexPath) as!PFObject
etc.

Swift and Parse - Message app, cannot display username within Xcode 6.3

I have some issue to display the 'author's username of each posts in my TableViewController.
It actually display the current user's username for all display posts, how to display each poster's username ?
I'm using Xcode 6.3 and Parse.com API.
The timeLabel is displayed correctly, but the userLabel display the current user who is logged in instead of the author of the post.
If I logged out and login with a different username all the userLabel change to the new user. The debug console display Optional("theNameOfTheCurrentUser") as many times as there are posts displayed.
Parse host 2 DB one for users (User) and one for posts (Poemes), there is a pointer in Poemes table to the specific user.
I update to Xcode 6.3 lately and had an error on var findLover:PFQuery = PFUser.query()
Value of optional type 'PFQuery?' not unwrapped
I add the exclamation mark (!) at the end of this line, which remove the error, is this causing the issue ?
I read Parse documentation and follow some exemples but looks like I'm a bit lost here, any help and suggestions will be highly appreciated, thanks.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:DisplayTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! DisplayTableViewCell
let poeme:PFObject = self.timelineData.objectAtIndex(indexPath.row) as! PFObject
cell.poemeLabel.text = poeme.objectForKey("content") as? String
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd HH:mm"
cell.timeLabel.text = dateFormatter.stringFromDate(poeme.createdAt!)
var findUser:PFQuery = PFUser.query()!
findUser.findObjectsInBackgroundWithBlock {
(objects, error)->Void in
if var objects = objects {
let author:PFUser = (objects as NSArray).lastObject as! PFUser
cell.userLabel.text = author.username
println(author.username)
})
}
return cell
}
The function findUser.findObjectsInBackgroundWithBlock happens in the background, while the main thread still running, so by the time you get the response from parse with the values you need the cell you are trying to return in the function is long gone.
The easiest way to fix it is to fetch all the data you need before hand and safe it in a array and use this array to populate the cell.
Finally get it work for Xcode 6.3.2 changes, here is the result :
unwrap and optional seams to be my main problem :
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
let cell:DisplayTableViewCell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath!) as! DisplayTableViewCell
let poeme:PFObject = self.timelineData.objectAtIndex(indexPath!.row) as! PFObject
cell.poemeLabel.text = poeme.objectForKey("content") as! String
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd HH:mm"
cell.timeLabel.text = dateFormatter.stringFromDate(poeme.createdAt!)
var findUser:PFQuery = PFUser.query()!
findUser.whereKey("objectId", equalTo: poeme.objectForKey("user")!.objectId!!)
findLover.findObjectsInBackgroundWithBlock {
(objects, error)->Void in
if var objects = objects {
let author:PFUser = (objects as NSArray).lastObject as! PFUser
cell.userLabel.text = author.username
println(author.username)
})
}
}
return cell
}

UITableView and Parse Data

I am having issues setting with Parse data and a UITableView. Everytime I run the application, whether on the simulator or my phone, the app stops working and the only thing the console shows is (lldb). When checking the debugger, only following two lines of code are highlighted.
findTimelineData.findObjectsInBackgroundWithBlock{
and
self.timelineData = array as NSMutableArray
Both have the error: Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT,subcode=0x0) and im not quite sure what that means...
Here are snippets of my code:
override func viewDidAppear(animated: Bool)
{
self.loadData()
}
func loadData()
{
timelineData.removeAllObjects()
var findTimelineData : PFQuery = PFUser.query()
findTimelineData.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]!, error: NSError!)-> Void in
if error == nil
{
println("No error")
if let object = objects as? [PFObject!]
{
for object in objects
{
self.timelineData.addObject(object)
}
}
let array : NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
}
}
and these would be the cells that i'm trying to load, but the code never gets this far...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
println("loading cell")
let postCell : LocationTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as LocationTableViewCell
let post : PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
postCell.imageView.image = post.objectForKey("currentLocation") as? UIImage
postCell.userInfo.text = post.objectForKey("FirstLastName") as? String
// Configure the cell...
return postCell
}
for the full code: http://pastebin.com/MN7qcFhq
this worked for me:
in your code just only replace
self.timelineData = array as NSMutableArray
to
self.timelineData = NSMutableArray(array: array)
Aren't you missing an s in:
if let object = objects as? [PFObject!]
{
for object in objects
{
self.timelineData.addObject(object)
}
}
Should be:
if let objects = objects as? [PFObject!]
{
for object in objects
{
self.timelineData.addObject(object)
}
}
Hey Buddy I got you on this one. I had the same issues but I got help and got it figured out.
This is your Load Function
Try this
func loadData(){
timelineData.removeAll(keepCapacity: false)
var findTimelineData:PFQuery = PFQuery(className:"Sweets")
findTimelineData.findObjectsInBackgroundWithBlock
{
(objects:[AnyObject]! , error:NSError!) -> Void in
if error == nil
{
self.timelineData = objects.reverse() as [PFObject]
//let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
println(objects)
// self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
}
}
Then go to your
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
And change
let sweet:PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
To This
let sweet: PFObject = self.timelineData[indexPath.row] as PFObject
Whats going on is you are mixing some Obj C with Swift , the code you are following was probably written in Beta so make sure you go back and learn the differences. If you are still having problems check all your conditions if(error != nil) should not be in your code.

Resources