I continue refining the implementation of my UICollectionViewController with Parse and this time I'm dealing with an issue that it might be related to cache or maybe the reloadData method itself.
Maybe you can help me identify the source of this strange behavior that I better show you on a short video to save time:
Refreshing issue video
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refershControlAction) forControlEvents:UIControlEventValueChanged];
[self.collectionView addSubview:refreshControl];
[self queryForTable];
}
Then on my refreshControlAction:
- (void)refershControlAction
{
NSLog(#"Reload grid");
// The user just pulled down the collection view. Start loading data.
[self queryForTable];
[refreshControl endRefreshing];
}
The query method is like this:
- (void)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:#"Photo"];
query.limit = 15;
[query orderByAscending:#"createdAt"];
[query setCachePolicy:kPFCachePolicyNetworkOnly];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d photos.", objects.count);
[self.collectionView reloadData];
gridImages = [[NSMutableArray alloc] initWithCapacity:objects.count];
// Do something with the found objects
for (PFObject *object in objects) {
PFFile *thumbnail = [object objectForKey:#"thumbnail"];
[thumbnail getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
// Now that the data is fetched, update the cell's image property with thumbnail
//NSLog(#"Fetching image..");
[gridImages addObject:[UIImage imageWithData:data]];
//NSLog(#"Size of the gridImages array: %d", [gridImages count]);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
This doesn't happen on my PFQueryTableViewController where I'm performing the exact same query and where I'm also using the iOS 6 refresh control instead of the one provided by Parse.
Do you see something that could be causing this behavior?
I could see some prob in your code.
- (void)refershControlAction
{
NSLog(#"Reload grid");
// The user just pulled down the collection view. Start loading data.
[self queryForTable];
[refreshControl endRefreshing];
}
you endRefreshing before your query get completed, so it is wrong use. You should put [refreshControl endRefreshing] in your-(voi)queryForTable` when the query complete
The other problem is I don't know if you get your datasource updated when the query completed.
Related
This is the code I have as part of my ViewDidLoad
I'm trying to do something in the part of the code where i have the NSLogs which aren't be executed. I haven't been able to find anyone with the same issue?
Where am I going wrong? Thanks in advance!
PFRelation *relation = [staffMember relationForKey:#"completedTraining"];
PFQuery *query = [relation query];
[query includeKey:#"trainingRecordPointer"];
[query findObjectsInBackgroundWithBlock:^(NSArray *completedTrainingRecords, NSError *error){
if(!error){
for (PFObject* completedTrainingRecord in completedTrainingRecords) {
PFObject * recordWtihTypeAndName = [completedTrainingRecord objectForKey:#"trainingRecordPointer"];
PFObject *outputObject = [[PFObject alloc]initWithClassName:#"NewTrainingRecord"];
NSString *recordName = [recordWtihTypeAndName valueForKey:#"RecordName"];
[completeRecordsWithType addObject:recordName];
[outputObject addObject:recordName forKey:#"recordName"];
[outputObject addObject:completedTrainingRecord.createdAt forKey:#"date"];
[[trainingRecordsDictionary objectForKey:[recordWtihTypeAndName objectForKey:#"Type"]] addObject:outputObject];
[self.tableView reloadData]; //it works up to this point but if I move this line outside
//the for-loop nothing happens
NSLog(#"this will execute"); // does execute
}
NSLog(#"this wont execute"); // doesn't execute
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
NSLog(#"this wont execute"); // doesn't execute
}];
You should move [self.tableView reloadData]; to outside your for-loop.
You should also make sure the tableview is reloaded on the mainthread, and not in this background thread.
Maybe like this:
[query findObjectsInBackgroundWithBlock:^(NSArray *completedTrainingRecords, NSError *error){
if(!error){
for (PFObject* completedTrainingRecord in completedTrainingRecords) {
... do your stuff ...
}
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^ {
[weakSelf.tableView reloadData];
});
}
}];
You probably get into trouble because you try to modify your UI on a backgroundthread.
This is related to a recent thread Update progress with MRProgress. I converted my cloudkit queries from the convenience API to CKOperations as a result of previous thread (Thanks Edwin!). So while using CKModifyRecordsOperation to save a record, I can see the record's progress via logging in the perRecordProgressBlock, which is great. However, I'm trying to send this progress back to the viewcontroller and I cannot figure out how to do that. I have created a class for all of my CloudKit methods - CKManager. The other problem I'm having is that I'm unsure when to update the progress indicator (using MRProgress framework) in the VC. Do I call it before, during or after the save operations call in the CKManager? Should it be called recursively until the progress == 1.0? Here is the code I have so far...every works fine except for updating/animating the progress indicator (it appears and shows 0% and then disappears when the save operation is completed). Also, I'm using a property (double progress) in my CKManager class and I know that is incorrect, but I wasn't sure how else to do it. And I do not feel that the callback method I've declared/defined in my CKManager class below for this is correct either. Any guidance is appreciated!
CKManager.h
#property (nonatomic, readonly) double progress;
- (void)recordProgressWithCompletionHandler:(void (^)(double progress))completionHandler;
CKManager.m
#property (nonatomic, readwrite) double progress;
- (void)recordProgressWithCompletionHandler:(void (^)(double))completionHandler {
completionHandler(self.progress);
}
- (void)saveRecord:(NSArray *)records withCompletionHandler:(void (^)(NSArray *, NSError *))completionHandler {
NSLog(#"INFO: Entered saveRecord...");
CKModifyRecordsOperation *saveOperation = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:records recordIDsToDelete:nil];
saveOperation.perRecordProgressBlock = ^(CKRecord *record, double progress) {
if (progress <= 1) {
NSLog(#"Save progress is: %f", progress);
self.progress = progress;
}
};
saveOperation.perRecordCompletionBlock = ^(CKRecord *record, NSError *error) {
NSLog(#"Save operation completed!");
completionHandler(#[record], error);
};
[self.publicDatabase addOperation:saveOperation];
}
Viewcontroller.m - this is from the method that takes the photo from the camera and calls the CKManager class to prepare the record and save it to CK as well as display the MRProgress indicator...
if (self.imageDataAddedFromCamera) {
self.hud = [MRProgressOverlayView showOverlayAddedTo:self.myCollectionView animated:YES];
self.hud.mode = MRProgressOverlayViewModeDeterminateCircular;
self.hud.titleLabelText = UPLOADING_MSG;
// prepare the CKRecord and save it
[self.ckManager saveRecord:#[[self.ckManager createCKRecordForImage:self.imageDataAddedFromCamera]] withCompletionHandler:^(NSArray *records, NSError *error) {
if (!error && records) {
NSLog(#"INFO: Size of records array returned: %lu", (unsigned long)[records count]);
CKRecord *record = [records lastObject];
self.imageDataAddedFromCamera.recordID = record.recordID.recordName;
NSLog(#"INFO: Record saved successfully for recordID: %#", self.imageDataAddedFromCamera.recordID);
[self.hud dismiss:YES];
[self.hud removeFromSuperview];
[self.imageLoadManager addCIDForNewUserImage:self.imageDataAddedFromCamera]; // update the model with the new image
// update number of items since array set has increased from new photo taken
self.numberOfItemsInSection = [self.imageLoadManager.imageDataArray count];
[self updateUI];
} else {
NSLog(#"Error trying to save the record!");
NSLog(#"ERROR: Error saving record to cloud...%#", error.localizedDescription);
[self.hud dismiss:YES];
[self.hud removeFromSuperview];
[self alertWithTitle:YIKES_TITLE andMessage:ERROR_SAVING_PHOTO_MSG];
}
}];
// where does this call belong?
[self.ckManager recordProgressWithCompletionHandler:^(double progress) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Updating hud display...");
[self.hud setProgress:progress animated:YES];
});
}];
You should include the progress handler in your saveRecord call like this:
- (void)saveRecord:(NSArray *)records withCompletionHandler:(void (^)(NSArray *, NSError *))completionHandler recordProgressHandler:(void (^)(double))progressHandler {
NSLog(#"INFO: Entered saveRecord...");
CKModifyRecordsOperation *saveOperation = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:records recordIDsToDelete:nil];
saveOperation.perRecordProgressBlock = ^(CKRecord *record, double progress) {
if (progress <= 1) {
NSLog(#"Save progress is: %f", progress);
progressHandler(progress)
}
};
saveOperation.perRecordCompletionBlock = ^(CKRecord *record, NSError *error) {
NSLog(#"Save operation completed!");
completionHandler(#[record], error);
};
[self.publicDatabase addOperation:saveOperation];
}
Then you can call that save record like this:
[self.ckManager saveRecord:#[[self.ckManager createCKRecordForImage:self.imageDataAddedFromCamera]] withCompletionHandler:^(NSArray *records, NSError *error) {
if (!error && records) {
NSLog(#"INFO: Size of records array returned: %lu", (unsigned long)[records count]);
CKRecord *record = [records lastObject];
self.imageDataAddedFromCamera.recordID = record.recordID.recordName;
NSLog(#"INFO: Record saved successfully for recordID: %#", self.imageDataAddedFromCamera.recordID);
[self.hud dismiss:YES];
[self.hud removeFromSuperview];
[self.imageLoadManager addCIDForNewUserImage:self.imageDataAddedFromCamera]; // update the model with the new image
// update number of items since array set has increased from new photo taken
self.numberOfItemsInSection = [self.imageLoadManager.imageDataArray count];
[self updateUI];
} else {
NSLog(#"Error trying to save the record!");
NSLog(#"ERROR: Error saving record to cloud...%#", error.localizedDescription);
[self.hud dismiss:YES];
[self.hud removeFromSuperview];
[self alertWithTitle:YIKES_TITLE andMessage:ERROR_SAVING_PHOTO_MSG];
}
}, recordProgressHandler:^(double progress) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Updating hud display...");
[self.hud setProgress:progress animated:YES];
});
}];
So that the code for updating the progress is part of your saveRecord call.
The code above is not tested by me. So I hope I made no typo's
I'm writing an application that allows the users to take and store images on Parse. Thus far I've managed to accomplish saving the image array to Parse by using the following logic:
Take Image
Add Object to Array
Convert array to NSData
Convert NSData to PFFile
Set file upload destination (via unique objectId)
Upload PFFile to Parse
This is what the code looks like; please forgive the fact that it's in dismissViewController for now, I'm only trying to get it to save successfully:
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
_takenImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
[self dismissViewControllerAnimated:YES completion:^
{
// Add object to array: Working
[_tankImagesArray addObject:_takenImage];
NSLog(#"Number of images taken: %lu", (unsigned long)_tankImagesArray.count);
// Convert array to NSData Object
NSData *imageData = [NSKeyedArchiver archivedDataWithRootObject:_tankImagesArray];
// Convert NSData Object to PFFile
PFFile *imageFile = [PFFile fileWithData:imageData];
PFQuery *tankQuery = [PFQuery queryWithClassName:#"SavedTanks"];
_tankObject = [tankQuery getObjectWithId:_passedValue];
[_tankObject setObject:imageFile forKey:#"tankImages"];
[_tankObject save];
}];
}
Now, my question is: How exactly would I go about retrieving that file? My ultimate goal here is to allow the user to see images they've taken in the past and add to the list of pictures in the collection and upload them to the server. I'm just unsure of how to retrieve the file once its been uploaded and make sure the integrity is maintained.
Did you try:
PFQuery *query = [PFQuery queryWithClassName:#"SavedTanks"];
[query whereKey:#"tankImages" equalTo:#"your_image.jpg"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d images.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
PFQuery *query = [PFQuery queryWithClassName:#"SavedTanks"];
// Add constraints here to get the image you want (like the objectId or something else)
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
PFFile *imageFile = object[#"tankImages"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:imageData]; // Here is your image. Put it in a UIImageView or whatever
}
}];
}
} else {
// Log details of the failure
}
}];
In the .h file of your collection view you need to have something like below. Note that the one I built you could like an image and then sort liked images using a segment controller.
#import <UIKit/UIKit.h>
#import "UICollectionCell.h"
#import <Parse/Parse.h>
#interface ParseViewController : UIViewController {
NSArray *imageFilesArray;
NSMutableArray *imagesArray;
}
#property (weak, nonatomic) IBOutlet UICollectionView *imagesCollection;
- (IBAction)segmentSelected:(id)sender;
#property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedController;
#end
Then in the .m file of your collection view
#interface ParseViewController ()
#end
#implementation ParseViewController
#synthesize imagesCollection;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self queryParseMethod];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// code to add the number of images etc as per table view
-(void) queryParseMethod {
NSLog(#"start query");
PFQuery *query = [PFQuery queryWithClassName:#"collectionView"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
imageFilesArray = [[NSArray alloc] initWithArray:objects];
NSLog(#"%#", imageFilesArray);
[imagesCollection reloadData];
}
}];
}
#pragma mark - UICollectionView data source
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
// number of sections
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
// number of items
return [imageFilesArray count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// the custom cell we named for the reusable identifier
static NSString *cellIdentifier = #"imageCell";
UICollectionCell *cell = (UICollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
PFObject *imageObject = [imageFilesArray objectAtIndex:indexPath.row];
PFFile *imageFile = [imageObject objectForKey:#"imageFile"];
// show loading spinner
[cell.loadingSpinner startAnimating];
cell.loadingSpinner.hidden = NO;
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
NSLog(#"%#", data);
cell.parseImage.image = [UIImage imageWithData:data];
[cell.loadingSpinner stopAnimating];
cell.loadingSpinner.hidden = YES;
}
}];
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self likeImage:[imageFilesArray objectAtIndex:indexPath.row]];
}
-(void) likeImage:(PFObject *)object {
[object addUniqueObject:[PFUser currentUser].objectId forKey:#"favorites"];
[object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
NSLog(#"liked picture!");
[self likedSuccess];
}
else {
[self likedFail];
}
}];
}
-(void) likedSuccess {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Success" message:#"You have succesfully liked the image" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
-(void) likedFail {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unsuccesfull" message:#"You have been unable to like the image" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (IBAction)segmentSelected:(id)sender {
if (_segmentedController.selectedSegmentIndex == 0) {
[self queryParseMethod];
}
if (_segmentedController.selectedSegmentIndex == 1) {
[self retrieveLikedImages];
}
}
-(void) retrieveLikedImages {
PFQuery *getFavorites = [PFQuery queryWithClassName:#"collectionView"];
[getFavorites whereKey:#"favorites" equalTo:[PFUser currentUser].objectId];
[getFavorites findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
imageFilesArray = [[NSArray alloc] initWithArray:objects];
[imagesCollection reloadData];
}
}];
}
#end
Hope this is of some help to you.
All the above solutions are correct, but want to add one another way to have support of Image Caching of SDWebImage or any library like that.
On Successful completion you will be have PFFile, whose property "url" will return you actuall URL of Image where it is saved. You can use that to load image. Using this approach I was able to have Image Cacheing based on key as URL.
...
NSString *strUrl = pfFileObject.url;
...
...
[img sd_setImageWithURL:[NSURL URLWithString:strUrl]];
Why would you want to download photos from parse that the user already has them locally..?
I recommend you to use : https://github.com/AFNetworking/AFNetworking
you can also save local photos to cache so you access them easily so you dont need any downloading from parse...
now if you still want to download the photos from parse just make a normal query and download all the photos parse object and you will get in the PFObject the PFFile of your photos.
Example:
PFQuery *query = [PFQuery queryWithClassName:#"SavedTanks"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for(PFObject *obj in objects){
PFFile *file = [obj objectForKey:#"tankImages"];
// now you can use this url to download the photo with AFNetwork
NSLog(#"%#",file.url);
}
}
}];
I have a button for each cell, and once clicked it deletes each individual object from the Parse backend. I have it set to reloadData upon success in the block as seen here:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
PFRelation *relation = [self.currentUser relationforKey:#"watching"];
[[relation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
// There was an error
} else {
// NSLog(#"%#", objects);
self.watchingEvents = objects;
[self refreshTableView];
}
}];
}
-(IBAction) deleteButtonAction:(id) sender
{
[SVProgressHUD showWithStatus:#"Removing from Watch List..."];
PFRelation *relation = [self.currentUser relationforKey:#"watching"];
[relation removeObject:self.selectedEvent];
[self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error)
{
NSString *errorString = [[error userInfo] objectForKey:#"error"];
UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:#"Error" message:errorString delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlertView show];
}
else
{
[SVProgressHUD showSuccessWithStatus:#"Removed from Watch List!"];
[self refreshTableView];
}
}];
}
-(void)refreshTableView
{
[self.tableView reloadData];
}
But it doesn't seem to update the tableView, the cell still shows up (even though it's gone because if I navigate away and open the view again the viewWillAppear gets called with the same reloadData function and the items gone like how it should be. So I know the functions working, since it's clearly working in viewWillAppear. Not sure why it's not working once the success of the block is run, as the alert runs fine as does any NSLog message I put in?
Change you refresh method to:
-(void)refreshTableView
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
you are deleting the data from the backend but you are not deleting that object from self.watchingEvents. and i think u must be using self.watchingEvents to populate the date in cellForRowAtindex of your table. Please delete that object from self.watchingEvents too on deletion and your problem should be solved.
You need to use [tableView beginUpdates] before you do delete, insert or move actions and then [tableView endUpdates] at the end.
- (void)beginUpdates;
- (void)endUpdates;
https://developer.apple.com/Library/ios/documentation/UIKit/Reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/beginUpdates
I have a UITableViewController that is in charge of displaying a table full of employees. This data is being stored on a database on parse.com.
This is my UITableViewController, in which I just initiate the store:
-(id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nil bundle:nil];
if(self){
store = [[EmployeeStore alloc] init];
}
return self;
}
This is the EmployeeStore init method, in which i query for the employees:
-(id) init{
self = [super init];
if(self){
employees = [[NSMutableArray alloc] init];
[self fetchEmployeesFromDatabase];
}
return self;
}
fetchEmployeesFromDatabase, where I query for the employees.
-(void) fetchEmployeesFromDatabase{
PFQuery *query = [PFQuery queryWithClassName:#"Employee"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[employees addObject:object];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
I am successfully receiving them, however the problem is the query takes place in the background, and does not finish until after the table view is done loading, so the table view does not populate. I need to have the table reload its data after the query is complete but how do I go about doing this?
UITableViewController
-(id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nil bundle:nil];
if(self){
store = [[EmployeeStore alloc] init];
store.tableView = self.tableView;
}
return self;
}
EmployeeStore.h
#property (nonatomic, strong) UITableView *tableView;
EmployeeStore.m
-(id) init{
self = [super init];
if(self){
employees = [[NSMutableArray alloc] init];
[self fetchEmployeesFromDatabase];
}
return self;
}
and fetchEmployeesFromDatabase
-(void) fetchEmployeesFromDatabase{
PFQuery *query = [PFQuery queryWithClassName:#"Employee"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[employees addObject:object];
}
dispatch_async(dispatch_get_main_queue(), ^ {
[self.tableView reloadData];
});
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
When the load completes tell the table view to reload.
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[employees addObject:object];
}
dispatch_async(dispatch_get_main_queue(), ^ {
[tableView reloadData];
});
} else {
You need to ensure that your employees store is thread safe or is not accessed by the background and main threads concurrently.
-(void) fetchEmployeesFromDatabase{
PFQuery *query = [PFQuery queryWithClassName:#"Employee"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[employees addObject:object];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
[self performSelectorOnMainThread:#selector(reloadTableView) withObject:nil waitUntilDone:NO];
}];
}
-(void)reloadTableView{
[self.aTableview reloadData];
}
Now you load your tableview on mainthread. Try this.
Here the data gets populated into the table even before the table view is completed.
All you need to do here is place the statement [self fetchEmployeesFromDatabase]; inside viewDidLoad() method or whichever method which invokes your table creation .
I had this problem, except for collection view. After a lot of pain, this did the trick to reload the data via the main thread.
dispatch_async(dispatch_get_main_queue(), ^ {
[self.collectionView reloadData];
[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
});