I have a UITableViewController that I use to display the names of a list of local Business objects. I have used this UITableViewController
previously to read information from a .csv file and this was all working and displaying the Business names in my UITableViewController perfectly.
However, I have recently moved all this information to Parse.com and I am now trying to read and display the information from Parse.com in my current UITableViewController.
I am trying to refactor the code rather than use Parse.com's tutorials here:
https://www.parse.com/docs/ios_guide#ui-tables/iOS
In my viewDidLoad method, I call my own method [self initializeContent] which does the PFQuery to retrieve a list of objects that fit a certain criteria and then save the objects in an NSMutableArray.
This executes fine and I can see the Business objects are being retrieved through my NSLogs in my [self initializeContent] method and saved to a NSMutableArray (dataArray).
However, when stepping through the code, my dataArray is 0 in the numberOfSectionsInTableView method. Therefore I dont see the Business names displayed in the UITableViewController.
Does the numberOfSectionsInTableView get executed before the viewDidLoad method perhaps, meaning that my dataArray will always be 0?
I have stepped through the code but cant figure out where my issue is.
Below is my code - I have included as much as I can to help identify where my problem is.
BusinessDetailTableViewController.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface BusinessDetailTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *dataArray;
#property (nonatomic, strong) NSMutableArray *defaultBusinessArrayMutable;
#property (nonatomic, assign) int tagNumber;
#property (nonatomic, strong) NSString *className;
#end
BusinessDetailTableViewController.m
#import "BusinessDetailTableViewController.h"
#import "BusinessDetailContent.h"
#import "DetailViewController.h"
#import <Parse/Parse.h>
#interface BusinessDetailTableViewController ()
#end
#implementation BusinessDetailTableViewController
#synthesize dataArray;
#synthesize defaultBusinessArrayMutable;
#synthesize tagNumber;
#synthesize className;
- (void)viewDidLoad
{
[super viewDidLoad];
[self initializeArrays];
[self initializeContent];
}
- (void) initializeArrays
{
dataArray = [[NSMutableArray alloc] init];
defaultBusinessArrayMutable = [[NSMutableArray alloc] init];
}
- (void) initializeContent
{
NSLog(#"Class Name: %#", className); // Shows the correct className
[self queryParse:className];
}
- (void) queryParse:(NSString *) className
{
PFQuery *query = [PFQuery queryWithClassName:self.className];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error)
{
NSLog(#"Successfully Retrieved %d Objects.", (int)objects.count); // Shows how many objects are retrieved
for (PFObject *object in objects)
{
BusinessDetailContent *businessDetailContent = [[BusinessDetailContent alloc] init];
businessDetailContent.no = object[#"ID"];
businessDetailContent.companyName = object[#"Company_Name"];
businessDetailContent.address = object[#"Address"];
businessDetailContent.tel = object[#"Tel"];
businessDetailContent.email = object[#"Email"];
businessDetailContent.website = object[#"Website"];
businessDetailContent.details = object[#"Details"];
businessDetailContent.imageName = object[#"Image_ID"];
// The information here is retrieved correctly
NSLog(#"BUSINESS DETAIL CONTENT - ID: %#, Company Name: %#, Address: %#, Tel: %#, Email: %#, Website: %#, Details: %#, Image_Name: %#",
businessDetailContent.no, businessDetailContent.companyName, businessDetailContent.address, businessDetailContent.tel,
businessDetailContent.email, businessDetailContent.website, businessDetailContent.details,
businessDetailContent.imageName);
[defaultBusinessArrayMutable addObject:businessDetailContent];
NSString *strFromInt = [NSString stringWithFormat:#"%d", tagNumber];
if (!([strFromInt isEqualToString:#"3"] || [strFromInt isEqualToString:#"10"]))
{
[dataArray addObject:defaultBusinessArrayMutable];
NSLog(#"Default Business Array Mutable Count: %i", (int)dataArray.count);
}
}
}
}
else
{
NSLog(#"Error retrieving objects: %# %#", error, [error userInfo]);
}
}];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#" cellForRowAtIndexPath "); // This is not executed
static NSString *CellIdentifier = #"Cell";
NSLog(#"Cell Identifier: %#", CellIdentifier);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell
NSArray *cellArray = [dataArray objectAtIndex:indexPath.section];
BusinessDetailContent *businessDetailContent = [cellArray objectAtIndex:indexPath.row];
cell.textLabel.text = businessDetailContent.companyName;
NSLog(#"Cell Textlabel Text: %#", cell.textLabel.text);
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#" didSelectRowAtIndexPath ");
[self performSegueWithIdentifier:#"businessDetailSegue" sender:self];
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"numberOfRowsInSection returning: %d", (int)[dataArray count]);
return [dataArray count]; // dataArray = 0
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSMutableArray *array = [dataArray objectAtIndex:section];
return [array count];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"PrepareForSegue: %#", segue.identifier);
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([segue.identifier isEqualToString:#"businessDetailSegue"] && indexPath)
{
NSArray *selectedRowArray = [dataArray objectAtIndex:indexPath.section];
BusinessDetailContent *selectedContent = [selectedRowArray objectAtIndex:indexPath.row];
DetailViewController *detailController = segue.destinationViewController;
detailController.businessDetailContent = selectedContent;
}
}a
#end
EDIT: I have added [self.tableView reloadData]; to the end of my findObjectsInBackgroundWithBlock method as many have suggested and now I am getting to the numberOfSectionsInTableView method, but the dataArray is still 0. And it looks as though my code is executing twice as can be seen by the Console print?
For my dataArray in my queryParse method it returns 18 objects - but there should be only 9.
From the Console print Hotel Array Count starts at 2 and increments by 2 each time after that.
And my numberOfRowsInSection is now getting executed twice, but before the Parse.com query.
I am totally stuck with this.
#Hans Moolman, yes you are correct, You just need to call the reload method of table view after your parsing is done. i .e.
- (NSMutableArray *) queryParse:(NSString *) className
{
PFQuery *query = [PFQuery queryWithClassName:self.className];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error)
{
NSLog(#"Successfully Retrieved %d Objects.", (int)objects.count); // Shows how many objects are retrieved
for (PFObject *object in objects)
{
BusinessDetailContent *businessDetailContent = [[BusinessDetailContent alloc] init];
businessDetailContent.no = object[#"ID"];
businessDetailContent.companyName = object[#"Company_Name"];
businessDetailContent.address = object[#"Address"];
businessDetailContent.tel = object[#"Tel"];
businessDetailContent.email = object[#"Email"];
businessDetailContent.website = object[#"Website"];
businessDetailContent.details = object[#"Details"];
businessDetailContent.imageName = object[#"Image_ID"];
// The information here is retrieved correctly
NSLog(#"BUSINESS DETAIL CONTENT - ID: %#, Company Name: %#, Address: %#, Tel: %#, Email: %#, Website: %#, Details: %#, Image_Name: %#",
businessDetailContent.no, businessDetailContent.companyName, businessDetailContent.address, businessDetailContent.tel,
businessDetailContent.email, businessDetailContent.website, businessDetailContent.details,
businessDetailContent.imageName);
[defaultBusinessArrayMutable addObject:businessDetailContent];
NSString *strFromInt = [NSString stringWithFormat:#"%d", tagNumber];
if (!([strFromInt isEqualToString:#"3"] || [strFromInt isEqualToString:#"10"]))
{
[dataArray addObject:defaultBusinessArrayMutable];
NSLog(#"Default Business Array Mutable Count: %i", (int)dataArray.count);
}
}
}
}
else
{
NSLog(#"Error retrieving objects: %# %#", error, [error userInfo]);
}
}];
// This line to be added
[self.tableView reloadData];
}
If still problems exists, do let me know. Thanks.
The call to parse.com returns data asynchronously, so the view will have finished loading and done its check for row count before the data is received. When the data is received you need to tell the table view to check the row count again and refresh - this is what you're missing.
[self.tableView reloadData];
and it should go in queryParse:, at the end of the completion block of findObjectsInBackgroundWithBlock:.
Hi first you set bool value default to false i mean viewdidload.
By parsing at didfinishloading function set bool to true
In numberofrowsinsection
if(!bool)
{
///just hide the tableview
}
else
{
[data count];
}
Related
I am having trouble getting my UITableView to reload appropriately. My app sometime works and other times it crashes because one of the data elements is not properly stored in an array when the tableView tries to update. Can someone point me out the proper place to reload my tableView please.
- (void)viewDidLoad {
[super viewDidLoad];
[self queryForNewBooks];}
-(void)queryForNewBooks{
_bookNameArray = [[NSMutableArray alloc] init];
_authorNameArray = [[NSMutableArray alloc] init];
_isbnNumberArray = [[NSMutableArray alloc] init];
_bookImageData = [[NSMutableArray alloc] init];
PFQuery *query = [PFQuery queryWithClassName:#"BooksForSale"];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^( NSArray *objects, NSError *error) {
if (objects.count >=1) {
for (PFObject *object in objects) {
PFFile *imageFile = [object objectForKey:#"image"];
[imageFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) {
if (!error) {
NSData *data = result;
NSLog(#"HEYYYYYY");
if (data == NULL) {
}
else{
[ _bookImageData addObject:data];
//I have tried placing it here [self.tableView reloadData]
// NSLog(#"%#",[_bookImageData objectAtIndex:0]);
}
}
}
];
NSDictionary *bookNameDictionary = object[#"nameOfBook"];
NSDictionary *authorNameDictionary = object[#"Author"];
NSDictionary *bookImageDictionary = object [#"image"];
NSDictionary *isbnNumberDictionary = object [#"isbnNumber"];
NSString *objectID = [object objectId];
if (bookNameDictionary != NULL){
NSLog(#"Yo bro here is the book name %#",bookNameDictionary);
[_bookNameArray addObject:bookNameDictionary];
NSLog(#"number: %#", bookNameDictionary);
}
if (bookNameDictionary == NULL) {
[_bookNameArray addObject:#""];
NSLog(#"Blank space");
}
if (authorNameDictionary != NULL) {
[_authorNameArray addObject:authorNameDictionary];
// [_tableData addObject:ft];
NSLog(#"Author Name : %#",_authorNameArray);
// NSLog(#"the table data is %#",_tableData);
}
if (authorNameDictionary == NULL) {
[_authorNameArray addObject:#""];
NSLog(#"Blank space");
}
if (isbnNumberDictionary != NULL){
NSLog(#"Yo bro here is the isbn %#",isbnNumberDictionary);
[_isbnNumberArray addObject:isbnNumberDictionary];
NSLog(#"number: %#", isbnNumberDictionary);
//[self.tableview reloadData];
}
if (isbnNumberDictionary == NULL) {
[_isbnNumberArray addObject:#""];
NSLog(#"Blank space");
}
/* if (bookImageDictionary !=NULL){
[_bookImageData addObject:bookImageDictionary];
}
if (bookImageDictionary ==NULL){
[_bookImageData addObject:#""];
NSLog(#"Blank Space");
}*/
if (objectID != NULL) {
[_objectIDArray addObject:objectID];
NSLog(#"object id is : %#",objectID);
}
if (objectID ==NULL){
[_objectIDArray addObject:#"blank"];
}
}
}
// code
}];
//I have tried placing it here [self.tableView reloadData]
);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.bookNameArray count];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"TableViewCell";
TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"TableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSLog(#"Here are the book names, %#",_bookNameArray);
cell.bookNameLabel.text = [_bookNameArray objectAtIndex:indexPath.row];
cell.authorNameLabel.text = [_authorNameArray objectAtIndex:indexPath.row];
if ([_bookImageData objectAtIndex:indexPath.row] != NULL ) {
NSLog(#"it seems to work");
UIImage *image = [UIImage imageWithData: [_bookImageData objectAtIndex:indexPath.row]];
cell.bookImageLabel.image = image;
}
else{
NSLog(#"Error");
}
return cell;
}
Updating question:
Is this the proper way to declare a PFimageView?
Can I just drag a UIImageView in the Xib file and then
change its class to PFImageView?
After changing it to a PFImageView should I be able to just link the view to the outlet as normally done?
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import <ParseUI/ParseUI.h>
#interface TableViewCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *bookNameLabel;
#property (nonatomic, weak) IBOutlet UILabel *authorNameLabel;
#property (nonatomic, weak) IBOutlet PFImageView *bookImageLabel;
#property (nonatomic, weak) IBOutlet UILabel *priceOfBook;
#end
#import <Parse/Parse.h>
#import "TableViewCell.h"
#import <ParseUI/ParseUI.h>
#implementation TableViewCell
#synthesize bookNameLabel =_bookNameLabel;
#synthesize authorNameLabel = _authorNameLabel;
#synthesize bookImageLabel = _bookImageLabel;
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
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 the PFFile 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). Your cellForRowAtIndexPath: 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];
I am purposely creating a empty Array to not display anything on the UITablewView.
However, it gives me that error.
To debug, I even created an empty UITableViewController and refer storyboard file to this. However, it is giving me the same error.
I just tried and connect it with an empty UIViewController, it is giving me the same objectAtIndex error.
So I doubt it is the problem with the what I am indexing for cells.
When I run, the screen is shown but it throws the error and it freezes.
The declaration of the newsList is:
#property (strong, nonatomic)NSArray *newsList
This is what I have for the UITableViewController.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.currentUser = appDelegate.currentUser;
NSString *addNewsFeed = #"_NewsFeed";
if (self.currentUser)
{
if (appDelegate.selectedGroup == nil)
{
self.newsList = nil;
}
else
{
NSLog(#"SELECTED GROUP EXIST");
NSString *currentNewsFeed = [appDelegate.selectedGroup[#"name"] stringByAppendingString:addNewsFeed];
PFQuery *query = [PFQuery queryWithClassName:currentNewsFeed];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error)
{
NSLog(#"Error: %#, %#", error, [error userInfo]);
}
else
{
self.newsList = objects;
[self.tableView reloadData];
}
}];
}
}
else
{
NSLog(#"%#", appDelegate.currentUser);
[self performSegueWithIdentifier:#"loginView" sender:self];
}
NSLog(#"ZXCVZCVZ: %#", self.newsList);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (appDelegate.selectedGroup == nil)
{
NSLog(#"NO CELL HERE");
return 0;
}
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (appDelegate.selectedGroup == nil)
{
NSLog(#"NO CELL");
return 0;
}
return [self.newsList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"NO LIST FOUND");
static NSString *CellIdentifier = #"News";
NSLog(#"DSFSDFSDFSFS");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFObject *item = [self.newsList objectAtIndex:indexPath.row];
cell.textLabel.text = item[#"title"];
cell.detailTextLabel.text = item[#"news"];
return cell;
}
you need to allocate the memory for array as below
self.newsList=[[NSMutableArray alloc]init];//At viewWIllAppear
Without alloc the self.newsList you cannot able to store any records in it...
Hope it fixes...
I have a view controller that retrieves address information from my web service, stores it in a mutable array and then displays each address in a table view. I have a search bar on the same view controller that i'd like to search through each of the addresses and display the results. I have this working with a test NSArray, however I'm not sure what I need to do to the filterContentForSearchText function to get it to search through an NSMutableArray. Any help appreciated.
Object Class
// Branches.h
#interface Branches : NSObject
#property (nonatomic, retain) NSString *BranchAddress;
#end
View Controller Class
// ViewController.h
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
.
// ViewController.m
#import "AFHTTPRequestOperationManager.h"
#import "ViewController.h"
#import "Branches.h"
#interface ViewController () {
NSMutableArray *array;
}
#property (strong, nonatomic) NSArray *searchResults;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize myArray
array = [[NSMutableArray alloc] init];
// Set POST parameters
NSDictionary *parameters = #{#"key" : #"value"};
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:#"webservice_address" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Check to see if responseObject contains data
if (responseObject != nil) {
// Loop through JSON
for (NSDictionary *dictionary in responseObject) {
// Initialize object
Branches *branches = [[Branches alloc] init];
branches.branchAddress = [dictionary objectForKey:#"Object"];
// Add object to myArray
[array addObject:branches];
[self.tableView reloadData];
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
#pragma Table View Methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchResults count];
} else {
return [array count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [[array objectAtIndex:indexPath.row] BranchAddress];
}
return cell;
}
#pragma Search Methods
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSArray *masterArray = array;
NSArray *searchResults = [masterArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Branches *evaluatedObject, NSDictionary *bindings) {
//NSLog(#"%#", evaluatedObject.BranchAddress);
return ([evaluatedObject.BranchAddress rangeOfString: searchText options:NSCaseInsensitiveSearch].location != NSNotFound);
}]];
NSLog(#" %i", searchResults.count);
//[searchResults removeAllObjects];
//[searchResults addObjectsFromArray:searchResults];
//reload after this
NSLog(#"%#", [searchResults objectAtIndex:0]);
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
#end
u can try this
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSArray *masterArray = self.array;
NSArray *resultsArray = [masterArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Branches *evaluatedObject, NSDictionary *bindings) {
return ([evaluatedObject.BranchAddress rangeOfString: searchText options:NSCaseInsensitiveSearch].location != NSNotFound);
}]];
//edited
[self.searchResults removeAllObjects]; //self.searchResults should be mutable array
[self.searchResults addObjectsFromArray:resultsArray];//put the new values to searchResults
//after this self.searchResults contains objects of filtered Branches u can get the values for example
NSLog(#"%#", [[self.searchResults objectAtIndex:0] BranchAddress]);//self.searchResults contains objects of Branches not the string itself
//reload after this
}
I followed this tutorial here https://parse.com/questions/using-pfquerytableviewcontroller-for-uitableview-sections and was able to create a beautiful table with sections, but it takes forever to load! Well, not forever, 5 minutes to be exact. My table in parse has 587 rows in it and it takes 5 minutes to load all of the objects into sections. The first few minutes shows the "Loading..." on the blank view, then there is an empty tableview, and finally all of the objects load. Is there a reason something like this is taking so long? I can't have my users wait 5 minutes for something to load. This tableview is displayed during the register process. It is a list of schools and the new user must select which school they are from. The sections organize the schools based on location, and there are about 30 sections. Any suggestions for getting this to load faster?
Here is the code for the SchoolFinderViewController.m file
#import "SchoolFinderViewController.h"
#interface SchoolFinderViewController ()
#property (nonatomic, retain) NSMutableDictionary *sections;
#property (nonatomic, retain) NSMutableDictionary *sectionToRegionMap;
#end
#implementation SchoolFinderViewController
#synthesize sections = _sections;
#synthesize sectionToRegionMap = _sectionToRegionMap;
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.parseClassName = #"School";
self.textKey = #"Name";
self.pullToRefreshEnabled = NO;
self.paginationEnabled = YES;
self.objectsPerPage = 600;
self.sections = [NSMutableDictionary dictionary];
self.sectionToRegionMap = [NSMutableDictionary dictionary];
}
return self;
}
-(void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Schools";
}
#pragma mark - PFQueryTableViewController
- (void)objectsDidLoad:(NSError *)error {
[super objectsDidLoad:error];
// This method is called every time objects are loaded from Parse via the PFQuery
NSLog(#"Count in objectsDidLoad: %lu", (unsigned long)[self.objects count]);
[self.sections removeAllObjects];
[self.sectionToRegionMap removeAllObjects];
NSInteger section = 0;
NSInteger rowIndex = 0;
int i = 0;
for (PFObject *object in self.objects) {
PFObject *obj = [object objectForKey:#"region"];
[obj fetchIfNeeded];
NSLog(#"School %#", [object objectForKey:#"Name"]);
NSString *Region = [obj objectForKey:#"name"];
NSLog(#"Reg: %#", Region);
NSMutableArray *objectsInSection = [self.sections objectForKey:Region];
if (!objectsInSection) {
objectsInSection = [NSMutableArray array];
NSLog(#"Is this called? %d", i);
// this is the first time we see this Region - increment the section index
[self.sectionToRegionMap setObject:Region forKey:[NSNumber numberWithInt:section++]];
}
[objectsInSection addObject:[NSNumber numberWithInt:rowIndex++]];
[self.sections setObject:objectsInSection forKey:Region];
}
NSLog(#"Finally done...");
}
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
// Order by name
[query orderByAscending:#"Name"];
return query;
}
- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath {
NSString *Region = [self RegionForSection:indexPath.section];
NSArray *rowIndecesInSection = [self.sections objectForKey:Region];
NSNumber *rowIndex = [rowIndecesInSection objectAtIndex:indexPath.row];
return [self.objects objectAtIndex:[rowIndex intValue]];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.sections.allKeys.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *Region = [self RegionForSection:section];
NSArray *rowIndecesInSection = [self.sections objectForKey:Region];
return rowIndecesInSection.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *Region = [self RegionForSection:section];
return Region;
}
#pragma mark - UITableViewDelegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSLog(#"CellFor %ld", (long)indexPath.row);
cell.textLabel.text = [object objectForKey:#"Name"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
PFObject *selectedObject = [self objectAtIndexPath:indexPath];
}
#pragma mark - ()
- (NSString *)RegionForSection:(NSInteger)section {
return [self.sectionToRegionMap objectForKey:[NSNumber numberWithInt:section]];
}
Yeah, you're not going to be able to make this fast enough as-is... The client should not have to download every object first, and scrolling lists with 500+ items are not a good user experience. Perhaps you should have an initial screen where they pick some subset, and then they can query a smaller set of data on the next screen. What you're currently using as a section might be a good candidate.
When I run the following code, nothing appears on my UITableView. I created a global NSMutableArray for storing the results of a query on Parse, but I can't manage to use that array to load the cells on the UITableView.
Thanks!
#import "ViewController.h"
#import "MenuViewController.h"
#import "Parse/Parse.h"
#interface ViewController ()
#end
#implementation ViewController {
CLLocationManager *locationManager;
}
#synthesize eventTableView;
#synthesize eventTableViewCell;
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadEvents];
}
- (void) loadEvents
{
eventNames = [[NSMutableArray alloc] init];
PFQuery *event_query = [PFQuery queryWithClassName:#"Event"];
[event_query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %lu scores.", (unsigned long)objects.count);
for (PFObject *object in objects) {
[eventNames addObject:[object objectForKey:#"event_name"]];
NSLog(#"%#", [object objectForKey:#"event_name"]);
NSLog(#"%lu", (unsigned long)eventNames.count);
}
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [eventNames count];
}
- (UITableViewCell *)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MainCell"];
}
NSString *cellText = [NSString stringWithFormat:#"%#",eventNames[indexPath.row]];
NSLog(#"%#", eventNames[indexPath.row]);
cell.textLabel.text = cellText;
return cell;
}
#end
Copy and paste the following loadEvents method
- (void) loadEvents
{
eventNames = [[NSMutableArray alloc] init];
PFQuery *event_query = [PFQuery queryWithClassName:#"Event"];
[event_query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %lu scores.", (unsigned long)objects.count);
for (PFObject *object in objects) {
[eventNames addObject:[object objectForKey:#"event_name"]];
NSLog(#"%#", [object objectForKey:#"event_name"]);
NSLog(#"%lu", (unsigned long)eventNames.count);
}
[eventTableView reloadData];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
Every time you asynchronously fetch data for table view, you have to reload the whole table or the changed sections.
I'm guessing that the result from findObjectsInBackground returned after the tableview has already displayed. Did the NSLog print out the results that you expected?
One thing to try is to add a
[eventTableView reloadData];
after the for loop , which will tell the tableview to reload the data again.