Not able to reload data properly when retrieving objects from Parse
I've had this problem before and fixed it by embedding a button in each cell. Inside your UITableView
you should try embedding each cell with a UIButton
.
First make a custom UITableViewCell
in a separate file. Then drag and make an IBOutlet
for your UIButton
inside your custom cell.
class MyCustomCell: UITableViewCell{
@IBOutlet weak var followButton: UIButton!
var isFollowing:Bool = false
//Declare other cell attributes here like picture, name, gender
// ......
}
When you query and gather the data for your cells, you can store them in an array in your UITableViewController
. For example, var myCellArray = [MyCustomCell]()
. Then your UITableViewController
will look something like this:
var myCellArray = [userListTableViewCell]()
override func viewDidLoad(){
super.viewDidLoad()
var userQuery = PFUser.query()
userQuery.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
if let usersArray = objects as! [PFUser] {
self.myCellArray.removeAll(keepCapacity: false)
for user in usersArray {
if let user = object as? PFUser {
if user.objectId != PFUser.currentUser()?.objectId {
var myCell = userListTableViewCell()
myCell.userID = user.objectId
myCell.username = user["fullName"] as! String
myCell.gender = user["gender"] as! String
var userPicture = user["profilePicure"] as? PFFile
var image = UIImage(data:userPicture!.getData()!)
myCell.displayPicture.image = image
myCellArray.append(myCell)
self.tableView.reloadData()
}
}
}
}
})
}
override func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
myCellArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier") as! userListTableViewCell
//Edit the storyboard labels for each cell:
cell.username.text = myCellArray[indexPath.row].username
// etc....
//Embed a button with each cell
cell.followButton.layer.setValue(indexPath.row, forKey: "index")
cell.followButton.addTarget(self, action: "followButtonTapped:", for ControlEvents: UIControlEvents.TouchUpInside)
if (myCellArray[indexPath.row].isFollowing == false){
cell.followButton.setTitle("Follow", forState: .Normal)
}else{
cell.followButton.setTitle("Unfollow", forState: .Normal)
}
return cell
}
func followButtonTapped(sender: UIButton){
let cellIndex : Int = (sender.layer.valueForKey("index")) as! Int
//You now have the index of the cell whose play button was pressed so you can do something like
if (myCellArray[cellIndex].isFollowing == false){
myCellArray[cellIndex] = true
}else{
myCellArray[cellIndex] = false
}
self.tableView.reloadData()
}
Where to Reload UITableView Data after parse query
The part that complicates the code is the need to fetch images using the objects returned by the query. The simplest solution is to omit the image fetching from the query completion handler.
Instead, loop the returned objects, building the arrays (there's room for improvement here, too, but just sticking with the crash part of your problem for now). In the
_bookImageData
array, don't try to keep images, instead keep thePFFile
for each object...// in the loop of objects, omit getDataInBackground and just do...
[_bookImageData addObject:object[@"image"]];The answer to the stated question -- where to reload the table -- is after the loop that builds the table datasource.
for (PFObject *object in objects) {
// code to build all of the arrays, omitting getDataInBackground
// ...
}
[self.tableView reloadData];In your table view cell, replace the image view with a
PFImageView
, because it can take care of fetching the image data for you (and other useful stuff like cacheing it). YourcellForRowAtIndexPath:
will then look like this...// bookImageLabel must be a PFImageView
// remember, we put the PFFile for each object into the _bookImageData array
cell.bookImageLabel.file = _bookImageData[indexPath.row];
[cell.bookImageLabel loadInBackground];
Swift why when Retrieving data from Parse I have to press button twice to load data in tableview
Your call to self.tableView.reloadData()
is in the wrong place and it's being called from the wrong queue. You need to call it on the main queue and it needs to be called inside the query completion handler after you update the data model.
query.findObjectsInBackground(block: { (objects : [PFObject]?, error: Error?) -> Void in
if error == nil {
for object in objects! {
if let userPicture = object.value(forKey: "photo") {
let userPicture = object.value(forKey: "photo")! as! PFFile
userPicture.getDataInBackground({ (imageData: Data?, error: Error?) -> Void in
let image = UIImage(data: imageData!)
if image != nil {
self.Saveddata.append(image!)
}
})
}
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
})
Retrieving data objects from localstorage and reload them into array
You've added that you are calling from onload
, and,
in your code you are loading into students
.
add to the beginning of your code:var students;
.
Note: Some Objects need special handling.
For example: Date
:
var date = new Date();
localStorage.date = JSON.stringify(date);
date = new Date(JSON.parse(localStorage.date));
Answer before additional information:
key
is a method of localStorage
.
Don't use it.
Use students
instead, for example.
Storage.key()
(This is assuming that you call the functions)
var students = [{name: "Petrina", age: "20"}];
function saveToLocalStorage(key, value){
localStorage.setItem(key, JSON.stringify(value));
console.log("Saved to LocalStorage");
}
function loadFromLocalStorage(key){
console.log("Loaded from LocalStorage");
return JSON.parse(localStorage.getItem(key));
}
console.log("students: "+students);
saveToLocalStorage("stu", students);
var st2=loadFromLocalStorage("stu");
console.log("st2: "+st2);
- cannot be run in a snippet: sandboxed, no access to localStorage - cross origin.
Difficulty retrieving Parse Object Field Values
You should refresh your tableview with this method;
self.tableView.reloadData()
You can put this code after your findObjectsInBackgroundWithBlock callback closure in your case;
projectRetrieval.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
//Append the productNames array with retrieved projectnames
self.productNames.append(object["projectname"] as! String)
// This line successfully prints the array of product Names retrieved.
print("Projectnames: \(self.productNames)")
}
}
self.tableView.reloadData()
})
Related Topics
How to Detect If My Device Is an iPhone x in Swift 4
How to Open Application Using Url
How to Configure Threshold/Distance When Swiping on Uitableviewcell
Ionic 3 Response with Status: 0 for Url: Null
How to Fetch Images from Photo Library Within Range of Two Dates in iOS
Translate Just 4 Lines of Code from Objective C to Swift (Pointers)
Check Whether Time Falls Between Two Time iOS
Detect Permission of Media Library iOS
Using '!' Here Is Deprecated and Will Be Removed in a Future Release - Swift 4.2
Camera Only Takes Pictures in Portrait Mode Swift
iOS - Running Background Task for Update User Location Using Swift
Scenekit Flattenedclone - Incorrect Use of Objc_Storeweak() and Objc_Loadweak() Error
How to Fix Crash When Tap to Select Row After Scrolling The Tableview
How to Create Custom Album in Swift 3 iOS 10
How to Check Bitfields (Scnetworkreachabilityflags in Particular) for Flags in Swift
Swift Unrecognized Selector Sent to Instance - What Am I Missing
Error When Instantiating a UIfont in an Text Attributes Dictionary