tableView cellForRowAtIndexPath: runs before ViewDidLoad? - ios

I am trying to display some arrays in a UITableViewController. I have a custom cell with a NIB. The problem I'm running into is my table rows try to populate before my arrays are loaded in my ViewDidLoad . Setting breakpoints show that my table rows are being created and then directly after my arrays are given values (which are set in my ViewDidLoad.
Can anyone tell me why the tableView method is running before the ViewDidLoad? What do I need to do to ensure my arrays are loaded before the table rows are populated?
Here is my ViewDidLoad :
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"go");
PFQuery *getCartQuery = [PFQuery queryWithClassName:#"chwCart"];
[getCartQuery whereKey:#"userId" equalTo:[PFUser currentUser].objectId];
[getCartQuery whereKey:#"status" equalTo:#"open"];
[getCartQuery getFirstObjectInBackgroundWithBlock:^(PFObject *currentCartObject, NSError *error) {
// NSLog([currentCartObject objectId]);
currentCart = [currentCartObject objectForKey:#"cartContents"];
dictFoodId = [currentCart valueForKey:#"foodId"];
dictQty = [currentCart valueForKey:#"qty"];
}];
NSLog(#"Should be loaded");
[self.tableView registerNib:[UINib nibWithNibName:#"foodCell" bundle:nil] forCellReuseIdentifier:#"foodCell"];
}
Here is my tableView cellForRowAtIndexPath:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"foodCell";
foodCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[foodCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.foodTitle.text = [dictQty objectAtIndex:indexPath.row];
return cell;
}

I think it's because you are updating the array into block which is running into background and your delegates called, just simply reload your tableView into block completion
[getCartQuery getFirstObjectInBackgroundWithBlock:^(PFObject *currentCartObject, NSError *error) {
// NSLog([currentCartObject objectId]);
currentCart = [currentCartObject objectForKey:#"cartContents"];
dictFoodId = [currentCart valueForKey:#"foodId"];
dictQty = [currentCart valueForKey:#"qty"];
[self.tableView reloadData];
}];

You are using blocks which does the process in background so your tableview is created and the delegate methods are called first you just add this line of code to your viewdidload code which will again call tableview after arrays are loaded
**[self.tableView reloadData];**
so your code in viewdidLoad should look like this
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"go");
PFQuery *getCartQuery = [PFQuery queryWithClassName:#"chwCart"];
[getCartQuery whereKey:#"userId" equalTo:[PFUser currentUser].objectId];
[getCartQuery whereKey:#"status" equalTo:#"open"];
[getCartQuery getFirstObjectInBackgroundWithBlock:^(PFObject *currentCartObject, NSError *error) {
// NSLog([currentCartObject objectId]);
currentCart = [currentCartObject objectForKey:#"cartContents"];
dictFoodId = [currentCart valueForKey:#"foodId"];
dictQty = [currentCart valueForKey:#"qty"];
[self.tableView reloadData];
}];
NSLog(#"Should be loaded");
[self.tableView registerNib:[UINib nibWithNibName:#"foodCell" bundle:nil] forCellReuseIdentifier:#"foodCell"];
}

Related

Referencing Array from other View Controller Table View

I am attempting to work a segmented control to display data from a search into categories. However although data is received it is not being displayed on my table. Here is my code for the two View Controllers. The child View controller is the one in which the UITableView is stored.
PARENT VC
- (void)searchPeople:(NSString*)text {
if(![text isEqualToString:#""]){
PFQuery *userWithName = [PFQuery queryWithClassName:#"_User"];
[userWithName whereKey:#"fullName" containsString:text];
PFQuery *userWithHandle = [PFQuery queryWithClassName:#"_User"];
[userWithHandle whereKey:#"username" containsString:text];
PFQuery *userQuery = [PFQuery orQueryWithSubqueries:#[userWithHandle,userWithName]];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
NSLog(#"USERS: %#",results);
[self.userResults removeAllObjects];
[self.userResults addObjectsFromArray:results];
[[ArrayManager sharedInstance].searchResults addObjectsFromArray:results];
NSLog(#"Count Number: %#", [ArrayManager sharedInstance].searchResults);
[[NSNotificationCenter defaultCenter] postNotificationName:#"reload_data" object:self];
}];
}
}
CHILD VC
-(void)handle_data {
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Object Entries: %lu", (unsigned long)[[ArrayManager sharedInstance].searchResults count]);
NSMutableArray * array = [[ArrayManager sharedInstance] getGlobalArray];
return [array count];
}
- (void)tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *data = [[ArrayManager sharedInstance]init].searchResults[indexPath.row];
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
cell.textLabel.text = data[#"objectId"];
return cell;
}
Data is returned fine from the server however, there is no data being displayed on the table. The ArrayManager class is a Singleton class.
Create a Data Access Object (DAO) and store all information in an array in that.
http://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm
Have both VC's change and access the DAO instead of each other. It is much simpler that way.

Some users not showing up in search parse iOS

I have a parse app for iOS where it is sometimes necessary to search all the users. For some reason though, the users exist in the database but other users cannot see them in a search. I have seen no correlation between users or reason for this the only thing I'm thinking is maybe parse is not searching ALL users? Here is the code for the search
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.parseClassName = #"_User";
self.textKey = #"name";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = NO;
}
return self;
}
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"isTeacher" equalTo:#"True"];
[query whereKey:#"schoolName" equalTo:[[PFUser currentUser] objectForKey:#"schoolName"]];
return query;
}
I'm assuming if there was a problem it would be in the above, but the rest of the code is here if need be:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.text = [object objectForKey:#"name"];
cell.detailTextLabel.text = [object objectForKey:#"username"];
}
// Configure the cell
if (tableView == self.tableView) {
cell.textLabel.text = [object objectForKey:#"name"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
PFObject* object = self.searchResults[indexPath.row];
//UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.textLabel.text = [object objectForKey:#"name"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *teacherUsername = cell.textLabel.text
;
//NSLog(teacherUsername);
[[NSUserDefaults standardUserDefaults] setObject:teacherUsername forKey:#"teacherUsername"];
[self performSegueWithIdentifier:#"next" sender:self];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
return self.objects.count;
} else {
return self.searchResults.count;
}
}
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"isTeacher" equalTo:#"True"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSArray *results = [NSArray arrayWithArray:objects];
NSLog(#"%#", results);
NSLog(#"%lu", (unsigned long)results.count);
NSLog(#"results^");
[self.searchResults addObjectsFromArray:results];
NSPredicate *searchPredicate =
[NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",searchTerm];
_searchResults = [NSMutableArray arrayWithArray:[results filteredArrayUsingPredicate:searchPredicate]];
[self.searchDisplayController.searchResultsTableView reloadData];
NSLog(#"%#", _searchResults);
NSLog(#"%lu", (unsigned long)_searchResults.count);
NSLog(#"search results^");
}];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#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.
}
*/
#end
Is there any reason why certain users would not show up? I have checked the obvious things, making sure the users have the same "schoolName" and "isTeacher" is true but I'm stumped. Attached is a screenshot of an example user in the parse core
The default limit for a Parse query is 100 objects so even though you expect 170 PFObjects, you need to specify that you want to receive 170+ objects from your query in order to receive them all from the query using the limit parameter, ex:
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"isTeacher" equalTo:#"True"];
[query setLimit: 1000]; // <-- increase the limit up to 1000
The upper limit for the number of PFObjects a PFQuery can return though is 1000, so since you have more than 1000 users and could hypothetically need to receive more than 1000 results when performing a different query, you can do so by looping through multiple queries while utilizing an increasing skip parameter, to specify the "number of objects to skip before returning any."
So whereas that first block of code I wrote will return the first 1000 objects from that query, the next 1000 can be retrieved like so:
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"isTeacher" equalTo:#"True"];
[query setLimit: 1000]; // <-- increase the limit up to 1000
[query setSkip: 1000]; // <-- skip the first 1000 already found
And generally speaking, although it's probably best to receive your results bit by bit and increment setSkip to receive more results only when you absolutely need them, you can hypothetically retrieve all the objects matching your query at once, like so:
- (void)theOriginalCallingMethod {
// Start out by fetching the maximum number of results
// from the query and start at the beginning, i.e.
// not skipping anything
[self performTeacherQueryWithLimit:1000 andSkip:0];
}
- (void)performTeacherQueryWithLimit:(int)limit andSkip:(int)skip {
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"isTeacher" equalTo:#"True"];
[query setLimit: limit];
[query setSkip: skip];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// If the maximum number of objects is found, there
// may be more, so continue querying
if (objects.count == limit) {
// Perform the query using the same limit, but increase
// the skip amount by that current limit to indicate
// that the next query should skip the results we just
// found
[self performTeacherQueryWithLimit:limit andSkip:skip+limit];
}
// ...other code...
}];
}
Note: This will only work with a PFTableView as long as its paginationEnabled property is set to NO.

Unable to get subclassed UITableViewCells to display data from successfully queried Parse objects

This question has been successfully answered; thank you to jsksma2.
I cannot get my data to fill the rows in my TableView, even though I get the data back properly and can hard-code the tableview to display a static amount of dummy text. I have a hunch my issue relates to initWithStyle vs initWithCoder for subclassed UITableViewCells.
In a subclass of UITableViewController called "GiveItemsTableViewC", during viewDidLoad I am querying Parse for objects each called "PFGiveItem". I get these back and add each one to a global variable, a mutable array called "myGiveItems". I log these, and I get what I am looking for, so that part is working.
GiveItemsTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
PFQuery *query = [PFQuery queryWithClassName:#"giveItem"];
[query whereKey:#"giver" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.myGiveItems = [[NSMutableArray alloc]init];
for (PFObject *object in objects) {
PFGiveItem *newGiveItem = [[PFGiveItem alloc]init];
newGiveItem.giveItemName = object[#"giveItemTitle"];
newGiveItem.giveItemImage = object[#"giveItemPhoto"];
[self.myGiveItems addObject:newGiveItem];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
Now I am trying to load each one of these giveItems into a TableView object, using custom TableViewCells each called "GiveItemCell."
GiveItemCell.m
#implementation JFGiveItemCell
#synthesize giveItemImageView = _giveItemImageView;
#synthesize giveItemLabel = _giveItemLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
Back in the table view controller, I return one section for the table view.
And when I include a static number for the rowsInSection, I can output test values to each cell. If I execute the code below, I will get a tableView with cells with the label of "Test", as per the upcoming cellForRowAtIndexPath method. So it works with that test, but obviously I'm looking to dynamically load the proper information.
GiveItemsTableViewController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 4;
}
- (JFGiveItemCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
JFGiveItemCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil){
cell = [[JFGiveItemCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
// PFGiveItem *giveItem = self.myGiveItems[indexPath.row];
// cell.giveItemLabel.text = giveItem.giveItemName;
cell.giveItemLabel.text = #"Test";
return cell;
}
It looks like you're forgetting to call [tableView reloadData] in the callback of your block method:
- (void)viewDidLoad
{
[super viewDidLoad];
PFQuery *query = [PFQuery queryWithClassName:#"giveItem"];
[query whereKey:#"giver" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.myGiveItems = [[NSMutableArray alloc]init];
for (PFObject *object in objects) {
PFGiveItem *newGiveItem = [[PFGiveItem alloc]init];
newGiveItem.giveItemName = object[#"giveItemTitle"];
newGiveItem.giveItemImage = object[#"giveItemPhoto"];
[self.myGiveItems addObject:newGiveItem];
}
[self.tableView reloadData];
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
Also, I second #CrimsonChris in saying that you need to set your dataSource methods properly:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.myGiveItems.count
}
There are a couple of problems...
Your numberOfRowsInSection should return the size of your myGiveItems array.
You need to tell your table view to reload when you finish loading your items asynchronously.
You don't need to implement number of sections, it defaults to 1.

Back on Navigation Controller causes Collection View items to duplicate

I think this is probably a simple fix, but I am struggling to find the right solution
I have the following:
ContactList - a UICollectionView that loads the content with viewDidAppear: method. When a item is selected in the collection view I fire this method:
[self performSegueWithIdentifier:#"pushContactDetail" sender:self];
So far so good. Everything works great. However when I navigate back from the ContactDetail page using the Navigation Controller button my ContactList duplicates the content.
Is there something I should be doing to prevent the viewDidAppear: from running again?
I don't think I want to set the collection to nil when I push the ContactDetail page as that would cause the content to be reloaded each time...
Here is the code:
-(void) viewDidAppear
{
[super viewDidAppear];
[self.view setBackgroundColor:myColorLightGrey];
_contactList = [[NSMutableArray alloc] init];
[self displayLoadingAndDisableTableViewInteractions];
[self queryData];
}
- (void) queryData
{
//Find the Data
PFQuery *query = [PFUser query];
PFUser *consumer = [PFUser currentUser];
[query includeKey:User_followers];
[query whereKey:#"email" equalTo:consumer.email];
query.maxCacheAge = 60 * 60 * 24; // One day, in seconds
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for (PFUser *tmpConsumer in objects)
{
for (PFUser *publisher in [tmpConsumer objectForKey:User_followers])
{
[_contactList addObject:publisher];
}
}
[_collectionView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"contactCell";
ContactCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
//SNIPPED REMAINDER OF CODE
...
}
In your queryData method, nil out the array first
_contactList = nil;
_contactList = [NSMutableArray array]; // or [[NSMutableArray alloc] init];
Move the alloc/init method for _contactList like this:
- (void) queryData
{
_contactList = nil;
_contactList = [[NSMutablArray alloc] init];
//Find the Data
PFQuery *query = [PFUser query];
PFUser *consumer = [PFUser currentUser];
[query includeKey:User_followers];
[query whereKey:#"email" equalTo:consumer.email];
query.maxCacheAge = 60 * 60 * 24; // One day, in seconds
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for (PFUser *tmpConsumer in objects)
{
for (PFUser *publisher in [tmpConsumer objectForKey:User_followers])
{
[_contactList addObject:publisher];
}
}
[_collectionView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}];
}
Navigate back with Animation true. It will solve your problem.

Parse, UITableView and reloaddata

I'm sitting on this almost 4 days and I can't find the problem (already searched google a lot and tried everything but nothing helped). I have this table view I created on my storyboard. Everything is connect BUT when I run my code:
First all the tableView method runs but since array still nil nothing happened. Then after my array got all the data and code says [tableView1 reloadData]; nothing happened and I'm not getting to the tableView methods... (I tried to locate the reloadData in many places in my code and nothing worked).
-(void)viewDidLoad {
[super viewDidLoad];
self.tableView1.dataSource=self;
self.tableView1.delegate=self;
self.tripNamesList = [[NSMutableArray alloc] init];
[self GetTripsList];
PFQuery *query = [PFQuery queryWithClassName:#"Trips"];
NSString *userID = user.objectId;
[query whereKey:#"User_Created_ID" equalTo:userID];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %d trips.", objects.count);
[self.tripNamesList addObjectsFromArray:objects];
NSLog(#"%#", tripNamesList);
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[self.tripNamesList addObject:object ];
}
[tableView1 reloadData];
}
else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.tripNamesList count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Calling 1222rrgdfghgfhdgfdfgfdgdfgd on %#", tableView);
static NSString *CellIdentifier = #"TripCell";
UITableViewCell *cell = [self.tableView1 dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
PFObject *obj2 = [self.tripNamesList objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"Trips"];
PFObject *searchedItems = [query getObjectWithId:obj2.objectId];
NSString *tempTripName = [searchedItems objectForKey:#"Trip_Name"];
cell.textLabel.text = tempTripName;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self.tableView1 reloadData];
return cell;
}
#end
Try running the reloadData from the main thread:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %d trips.", objects.count);
[self.tripNamesList addObjectsFromArray:objects];
NSLog(#"%#", tripNamesList);
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[self.tripNamesList addObject:object ];
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self.tableView1 reloadData];
});
}
else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
You dont need to call [tableView reloadData] everywhere. Once this method is called, the tableview's delegates will be called in order like numberOfRowsInSection and based on the number of rows, that much time cellForRowAtIndexPath will be called. So in your case, you have put reloadData in cellForRowAtIndexPath and it will create an infinte loop, I guess.
Also, since you are filling self.tripNamesList in background thread, you can call reloadData in main thread as like below
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[self.tripNamesList addObject:object ];
}
[self performSelectorOnMainThread:#selector(refreshTableData) withObject:nil waitUntilDone:NO];
Yes, for this you need to have a method like this in your .m
- (void) refreshTableData{
[tableView reloadData];
}
Hope this helps.
Try to move your code from viewdidload to viewdidlayout, viewwillappear or viewdidapear, your table might not be initialised yet at that point.

Resources