Had a little bit of trouble replacing my TableViewController with PFQueryTableViewController, but I got it working with pull to refresh and pagination. However, I can't get the cache to work (at the very bottom of the .m file). Any ideas on what's going on?
The only other change I made here was subclass the image in the cell as PFImage.
ContactsTableViewController.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import "FilterViewController.h"
#interface ContactsTableViewController : PFQueryTableViewController <UITableViewDelegate>
#property (nonatomic) NSString *titleName;
#property (nonatomic) NSString *date;
#property (nonatomic) NSString *price;
#property (nonatomic) NSString *imageName;
#property (nonatomic) NSString *venue;
#property (nonatomic) UIImage *image;
#property (nonatomic) NSString *filter;
#property (nonatomic) NSArray *eventsArray;
-(void)retrieveEvents;
-(void)filterEvents;
#end
ContactsTableViewController.m
#import "ContactsTableViewController.h"
#import "CustomCellTableViewCell.h"
#import "DetailViewController.h"
#interface ContactsTableViewController ()
#end
#implementation ContactsTableViewController
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// The className to query on
self.parseClassName = #"eventsList";
// The key of the PFObject to display in the label of the default cell style
// 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;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(retrieveEvents)];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(filterEvents) name:#"updateParent" object:nil];
self.view.backgroundColor = [UIColor colorWithRed:245/255.0 green:243/255.0 blue:240/255.0 alpha:1];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.eventsArray count];
}
// Parse Method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *CellIdentifier = #"cellID";
CustomCellTableViewCell *customCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *tempDict = [self.eventsArray objectAtIndex:indexPath.row];
self.titleName = [tempDict objectForKey:#"eventTitle"];
self.price = [tempDict objectForKey:#"eventPrice"];
self.date = [tempDict objectForKey:#"eventDate"];
self.venue = [tempDict objectForKey:#"eventVenue"];
PFFile *imageFile = [tempDict objectForKey:#"eventImage"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
customCell.customImageView.image = [UIImage imageWithData:data];
}
}];
customCell.titleLabel.text = self.titleName;
customCell.priceLabel.text = self.price;
customCell.customDateLabel.text = self.date;
customCell.venueNameLabel.text = self.venue;
return customCell;
}
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[cell setBackgroundColor:[UIColor darkGrayColor]];
tableView.separatorColor = [UIColor clearColor];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showEventDetail"]) {
DetailViewController *destinationVC = [[DetailViewController alloc] init];
destinationVC = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSDictionary *tempDict = [self.eventsArray objectAtIndex:indexPath.row];
destinationVC.eventTitle = [tempDict objectForKey:#"eventTitle"];
destinationVC.eventPrice = [tempDict objectForKey:#"eventPrice"];
destinationVC.eventDate = [tempDict objectForKey:#"eventDate"];
destinationVC.venueName = [tempDict objectForKey:#"eventVenue"];
destinationVC.navigationItem.title = [tempDict objectForKey:#"eventTitle"];
// Image
PFFile *imageFile = [tempDict objectForKey:#"eventImage"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
destinationVC.eventImageView.image = [UIImage imageWithData:data];
destinationVC.eventImage = [UIImage imageWithData:data];
}
}];
// GeoPoint
destinationVC.geoPoint = [tempDict objectForKey:#"GeoPoint"];
} else if ([segue.identifier isEqualToString:#"showFilterTable"]){
FilterViewController *vc = (FilterViewController *)[[[segue destinationViewController] viewControllers] objectAtIndex:0];
[vc setDelegate:self];
}
}
-(void)retrieveEvents
{
PFQuery *retrieveEvents = [PFQuery queryWithClassName:#"eventsList"];
[retrieveEvents setLimit:300];
[retrieveEvents orderByAscending:#"eventTitle"];
[retrieveEvents findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.eventsArray = [[NSArray alloc] initWithArray:objects];
}
[self.tableView reloadData];
}];
}
-(void)filterEvents
{
if ([self.filter isEqualToString:#"All Events"]) {
[self performSelector:#selector(retrieveEvents)];
return;
}
NSLog(#"retrieveEvents: %#", self.filter);
PFQuery *retrieveEvents = [PFQuery queryWithClassName:#"eventsList"];
[retrieveEvents whereKey:#"eventType" equalTo:self.filter];
[retrieveEvents setLimit:300];
[retrieveEvents orderByAscending:#"eventTitle"];
[retrieveEvents findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.eventsArray = [[NSArray alloc] initWithArray:objects];
NSLog(#"%#", self.eventsArray);
}
[self.tableView reloadData];
}];
}
// Parse Method
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
return query;
}
#end
From the looks of it you are not using the queryForTable to populate the tableview, you are using your separate query.
This query never gets used in your tableview
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
return query;
}
Instead this one does:
-(void)retrieveEvents
{
PFQuery *retrieveEvents = [PFQuery queryWithClassName:#"eventsList"];
[retrieveEvents setLimit:300];
[retrieveEvents orderByAscending:#"eventTitle"];
[retrieveEvents findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.eventsArray = [[NSArray alloc] initWithArray:objects];
}
[self.tableView reloadData];
}];
}
kPFCachePolicyCacheThenNetwork means to use both cache and network (first get from cache for quickness, then get from network for up-to-date-ness the next time). Try instead using kPFCachePolicyCacheElseNetwork.
Try this code. It may not be perfect. But I believe it should at least head you in the right direction.
#import "ContactsTableViewController.h"
#import "CustomCellTableViewCell.h"
#import "DetailViewController.h"
#interface ContactsTableViewController ()
#end
#implementation ContactsTableViewController
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// The className to query on
self.parseClassName = #"eventsList";
// The key of the PFObject to display in the label of the default cell style
// 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;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(filterEvents) name:#"updateParent" object:nil];
self.view.backgroundColor = [UIColor colorWithRed:245/255.0 green:243/255.0 blue:240/255.0 alpha:1];
tableView.separatorColor = [UIColor clearColor];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//This reloaded the PFQueryTableView
[self loadObjects];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.objects.count;
}
// Parse Method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *CellIdentifier = #"cellID";
CustomCellTableViewCell *customCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFObject *object = [self objectAtIndexPath:indexPath];
customCell.titleLabel.text = object[#"eventTitle"];
customCell.priceLabel.text = object[#"eventPrice"];
customCell.customDateLabel.text = object[#"eventDate"];
customCell.venueNameLabel.text = object[#"eventVenue"];
PFFile *imageFile = [tempDict objectForKey:#"eventImage"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
customCell.customImageView.image = [UIImage imageWithData:data];
}
}];
return customCell;
}
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[cell setBackgroundColor:[UIColor darkGrayColor]];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showEventDetail"]) {
DetailViewController *destinationVC = [[DetailViewController alloc] init];
destinationVC = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSDictionary *tempDict = [self.eventsArray objectAtIndex:indexPath.row];
destinationVC.eventTitle = [tempDict objectForKey:#"eventTitle"];
destinationVC.eventPrice = [tempDict objectForKey:#"eventPrice"];
destinationVC.eventDate = [tempDict objectForKey:#"eventDate"];
destinationVC.venueName = [tempDict objectForKey:#"eventVenue"];
destinationVC.navigationItem.title = [tempDict objectForKey:#"eventTitle"];
// Image
PFFile *imageFile = [tempDict objectForKey:#"eventImage"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
destinationVC.eventImageView.image = [UIImage imageWithData:data];
destinationVC.eventImage = [UIImage imageWithData:data];
}
}];
// GeoPoint
destinationVC.geoPoint = [tempDict objectForKey:#"GeoPoint"];
} else if ([segue.identifier isEqualToString:#"showFilterTable"]){
FilterViewController *vc = (FilterViewController *)[[[segue destinationViewController] viewControllers] objectAtIndex:0];
[vc setDelegate:self];
}
}
// Parse Method
-(NSString *)filter
{
return _filter;
}
-(void)setFilter
{
_filter = filter;
[self loadObjects];
}
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
if (self.filter && ![self.filter isEqualToString:#"All Events"]) [query whereKey:#"eventTitle" equalTo:self.filter];
[query setLimit:300];
[query orderByAscending:#"eventTitle"];
return query;
}
#end
Related
Explanation of what's taking place: The user has added a job as their favorite within another view. Now the user is in the Favorites tab and decides that they no longer want the job as one of their favorites anymore, so they swipe to delete the job. They tap the delete button and the errors below take place...the code works as is, but it also deletes every single job the user has saved as a favorite, instead of just deleting the one job.
My code also gives me an alert of:
Warning: A long-running operation is being executed on the main thread.
Break on warnBlockingOperationOnMainThread() to debug.
#import "JobDetailViewController.h"
#import "MyFavoritesTableViewController.h"
#import "Parse/Parse.h"
#import "Job.h"
#import "JobListViewController.h"
#interface MyFavoritesTableViewController ()
#property (nonatomic, strong) NSString *mainTitle;
#property (nonatomic, strong) NSString *subTitle;
#end
#interface MyFavoritesTableViewController ()
#end
#implementation MyFavoritesTableViewController
{}
#synthesize mainTitle;
#synthesize subTitle;
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if ([PFUser currentUser]) {
// Custom the table
// The className to query on
self.parseClassName = #"Jobs";
// The key of the PFObject to display in the label of the default cell style
self.textKey = #"Position";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = YES;
// The number of objects to show per page
self.objectsPerPage = 30;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)objectsWillLoad {
[super objectsWillLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.tableView reloadData];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object: (PFObject *)object
{
static NSString *myJobsTableIdentifier = #"myFavsCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:myJobsTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:myJobsTableIdentifier];
}
// Configure the cell
PFFile *thumbnail = [object objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
thumbnailImageView.image = [UIImage imageNamed:#"AppIcon.png"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
UILabel *positionLabel = (UILabel*) [cell viewWithTag:101];
positionLabel.text = [object objectForKey:#"Position"];
UILabel *rotationLabel = (UILabel*) [cell viewWithTag:102];
rotationLabel.text = [object objectForKey:#"Rotation"];
UILabel *locationLabel = (UILabel*) [cell viewWithTag:103];
locationLabel.text = [object objectForKey:#"Location"];
UILabel *typeLabel = (UILabel*) [cell viewWithTag:104];
typeLabel.text = [object objectForKey:#"Type"];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showDetailedView"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Job *job = [[Job alloc] init];
JobDetailViewController *destViewController = segue.destinationViewController;
PFObject *object = [self.objects objectAtIndex:indexPath.row];
job.position = [object objectForKey:#"Position"];
job.poc = [object objectForKey:#"POC"];
job.email = [object objectForKey:#"Email"];
job.phone = [object objectForKey:#"Phone"];
job.apply = [object objectForKey:#"Apply"];
job.imageFile = [object objectForKey:#"imageFile"];
job.rotation = [object objectForKey:#"Rotation"];
job.location = [object objectForKey:#"Location"];
job.type = [object objectForKey:#"Type"];
job.clearance = [object objectForKey:#"Clearance"];
job.job_description = [object objectForKey:#"Job_Description"];
job.qualifications = [object objectForKey:#"Qualifications"];
job.originalJob = object;
destViewController.job = job;
}
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([self.objects count] == indexPath.row) {
[self loadNextPage];
} else {
PFObject *photo = [self.objects objectAtIndex:indexPath.row];
NSLog(#"%#", photo);
// Do something you want after selected the cell
}
}
- (PFQuery *)queryForTable
{
PFUser *user = [PFUser currentUser];
PFRelation *relation = [user relationForKey:#"Favorites"];
PFQuery *myquery = [relation query];
if (self.pullToRefreshEnabled) {
myquery.cachePolicy = kPFCachePolicyNetworkOnly;
}
return myquery;
}
#pragma mark - DeleteJobViewDelegate
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
PFUser *user = [PFUser currentUser];
PFRelation *relation = [user relationForKey:#"Favorites"];
PFQuery *myquery = [relation query];
NSArray *array = [myquery findObjects];
for (PFObject *object in array)
{
[relation removeObject:object];
}
[user saveInBackground];
[self.tableView reloadData];
[self loadObjects];
}
#end
The problem is that you are removing all the objects from the Relation:
NSArray *array = [myquery findObjects];
for (PFObject *object in array)
{
[relation removeObject:object];
}
What your code is doing is going though your array and removing each object from the relation.
What you want to do is to delete the job for that cell only. You can get that using the indexPath:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
PFUser *user = [PFUser currentUser];
PFRelation *relation = [user relationForKey:#"Favorites"];
[relation removeObject:[self.objects objectAtIndex:indexPath.row]];
[user saveInBackground];
[self.tableView reloadData];
[self loadObjects];
}
Your second problem:
Warning: A long-running operation is being executed on the main thread. Break on warnBlockingOperationOnMainThread() to debug.
That warning is because findObjects is a synchronous call. You should use the findObjectsInBackground instead. But if you make the changes I gave above, you won't need it.
I'm using Parse to populate a table view which works fine. I'm trying to delete a row from the table view but get this error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI length]: unrecognized selector sent to instance 0x7fd3f0f20f60'
Here is the code:
#import "CompletedJobs.h"
#import <Parse/Parse.h>
#interface CompletedJobs ()
#property (nonatomic, strong) NSMutableArray *jobs;
#property (nonatomic, strong) NSMutableArray *objectIds;
#property (nonatomic, strong) UIActivityIndicatorView *loadingIndicator;
#property (nonatomic, strong) UIRefreshControl *refresh;
#end
#implementation CompletedJobs
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView setDataSource:self];
[self.tableView setDelegate:self];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
CGFloat width = CGRectGetWidth(self.view.bounds);
CGFloat height = CGRectGetHeight(self.view.bounds);
self.loadingIndicator = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(width / 2, height / 2, 37, 37)];
self.loadingIndicator.center = CGPointMake(width / 2, height / 2 - 37);
self.loadingIndicator.autoresizingMask = (UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin);
self.loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
self.loadingIndicator.hidesWhenStopped = YES;
[self.view addSubview:self.loadingIndicator];
[self.loadingIndicator startAnimating];
[self getJobs];
self.refresh = [[UIRefreshControl alloc]init];
self.refresh.tintColor = [UIColor blackColor];
[self.refresh addTarget:self action:#selector(refreshData) forControlEvents:UIControlEventValueChanged];
self.refreshControl = self.refresh;
}
- (void)viewDidAppear:(BOOL)animated {
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)getJobs {
self.jobs = [[NSMutableArray alloc]init];
self.objectIds = [[NSMutableArray alloc]init];
PFQuery *query = [PFQuery queryWithClassName:#"Completed"];
[query setLimit:1000];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (NSDictionary *objectDictionary in objects) {
[self.jobs addObject:objectDictionary];
for (int i = 0; i < self.jobs.count; i++) {
[self.objectIds addObject:[self.jobs valueForKeyPath:#"objectId"]];
}
}
dispatch_async(dispatch_get_main_queue(), ^ {
[self.tableView reloadData];
[self.loadingIndicator stopAnimating];
});
}
else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (void)refreshData {
[self getJobs];
[self.refresh endRefreshing];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return self.jobs.count;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSString *objectId = [self.objectIds objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"Completed"];
[query getObjectInBackgroundWithId:objectId block:^(PFObject *job, NSError *error) {
[job deleteInBackground];
}];
[self getJobs];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
NSDictionary *jobDictionary = [self.jobs objectAtIndex:[indexPath row]];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"UITableViewCell"];
}
if (cell) {
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.text = [jobDictionary objectForKey:#"job"];
NSString *completedBy = [jobDictionary objectForKey:#"completedBy"];
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
formatter.dateStyle = NSDateFormatterLongStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
NSString *completedDate = [formatter stringFromDate:[jobDictionary objectForKey:#"date"]];
NSString *detailString = [NSString stringWithFormat:#"Completed by: %# on %#", completedBy, completedDate];
cell.detailTextLabel.text = detailString;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
#end
When I run the application, the header view container space is there, and is visible. I'm trying to display the username inside the header view, but nothing is showing up. Here is my code so far in the tableView:
#import "HomeView.h"
#interface HomeView () <UIActionSheetDelegate, UIImagePickerControllerDelegate>
#end
#implementation HomeView
- (void)viewDidLoad {
[super viewDidLoad];
if ([PFUser currentUser]) {
NSLog(#"Welcome to the App, %#", [self.user objectForKey:#"username"]);
} else {
LoginView *loginView = [[LoginView alloc] init];
[loginView setHidesBottomBarWhenPushed:YES];
[loginView.navigationItem setHidesBackButton:YES];
[self.navigationController pushViewController:loginView animated:NO];
}
UIBarButtonItem *takePhoto = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:#selector(takePhoto:)];
self.navigationItem.rightBarButtonItem = takePhoto;
// [self.tableView registerClass:[HomeViewCell class] forCellReuseIdentifier:#"cellIdentifier"];
// [self.tableView registerNib:[UINib nibWithNibName:#"HomeViewCell" bundle:nil]
// forCellReuseIdentifier:#"cellIdentifier"];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
self.reusableSectionHeaderViews = [NSMutableSet setWithCapacity:3];
}
return self;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
PFQuery *query = [PFQuery queryWithClassName:#"UserPhotos"];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
} else {
self.userPhotos = objects;
NSLog(#"Retrieved objects: %#", self.userPhotos);
[self.tableView reloadData];
}
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger sections = self.userPhotos.count;
// Return the number of sections.
return sections;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 1;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
if (section == self.userPhotos.count) {
// Load More section
return nil;
}
HomeHeaderView *headerView = [self dequeueReusableSectionHeaderView];
if (!headerView) {
headerView = [[HomeHeaderView alloc] initWithFrame:CGRectMake( 0.0f, 0.0f, self.view.bounds.size.width, 44.0f) buttons:HomePhotoHeaderButtonsDefault];
headerView.delegate = self;
[self.reusableSectionHeaderViews addObject:headerView];
}
//Setting the username display.
PFObject *owner = [self.userPhotos objectAtIndex:section];
PFUser *user = [owner objectForKey:#"user"];
[user fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
NSString *username = user.username;
[headerView.userButton setTitle:username forState:UIControlStateNormal];
}
}];
return headerView;
}
- (HomeHeaderView *)dequeueReusableSectionHeaderView {
for (HomeHeaderView *sectionHeaderView in self.reusableSectionHeaderViews) {
if (!sectionHeaderView.superview) {
// we found a section header that is no longer visible
return sectionHeaderView;
}
}
return nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"cellIdentifier";
HomeViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[HomeViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Setting the image in the cell.
PFObject *carPhoto = [self.userPhotos objectAtIndex:indexPath.row];
PFFile *imageFile = [carPhoto objectForKey:#"imageFile"];
NSURL *imageFileUrl = [[NSURL alloc] initWithString:imageFile.url];
NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
cell.carImage.contentMode = UIViewContentModeScaleAspectFit;
cell.carImage.image = [UIImage imageWithData:imageData];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 269;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (section == self.userPhotos.count) {
return 0.0f;
}
return 44.0f;
}
[user fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
NSString *username = user.username;
[headerView.userButton setTitle:username forState:UIControlStateNormal];
}
}];
The code above, you are updating the headerview UI from a background thread. You need to wrap the UI part so it performs on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[headerView.userButton setTitle:username forState:UIControlStateNormal];
};
You need to be careful though, you are performing a background operation and then updating the header which you are reusing. There is a chance that the header is reused for a new section before the operation finishes and it is then set with the wrong username for that section. You can take care of this by not reusing the header or canceling the network operation if the header scrolls off screen before the operation finishes.
I have a well-functioning search bar. Only one thing that I want to improve is NSTimer. I don`t know why, but when I start typing, it search me results after some delay.It woks perfectly. But I get for every typed letter 1 API each. It mean that if I put into search bar for example Sudan. I get 5 API instead of 1. Could you someone help me?
Thanks
TableViewControllerTOP.h
#interface TableViewControllerTOP : UITableViewController<UISearchDisplayDelegate, UISearchBarDelegate,UIScrollViewDelegate,UITableViewDataSource, UITableViewDelegate>
{
NSTimer *myTimer;
}
#property (strong, nonatomic) IBOutlet UITableView *MainTable;
#property (nonatomic, strong) UISearchBar *searchBar;
#property (nonatomic, strong) UISearchDisplayController *searchController;
#property (nonatomic, strong) NSMutableArray *searchResults;
#property (nonatomic, retain) NSTimer *myTimer;
#property (nonatomic, assign) BOOL isAscending;
TableViewControllerTOP.m
#interface TableViewControllerTOP ()
#property (strong,nonatomic) NSMutableArray *itemss;
#end
#implementation TableViewControllerTOP
#synthesize myTimer;
#synthesize searchBar;
#synthesize searchController;
#synthesize searchResults;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.isAscending = YES;
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; [refreshControl addTarget:self action:#selector(pulltorefresh) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
PFQuery *query = [PFQuery queryWithClassName:#"Countries"];
query.cachePolicy = kPFCachePolicyIgnoreCache;
[query addDescendingOrder:#"createdAt"];
[query setLimit:10];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableArray *items = [NSMutableArray array];
for (id obj in objects)
{
[items addObject:obj];
}
self.itemss = items;
NSLog(#"%#", objects);
NSLog(#"%lu", (unsigned long)[objects count]);
[_MainTable reloadData];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
[self searchitems];
}
-(void)searchitems
{
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.tableView.tableHeaderView = self.searchBar;
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
self.searchResults = [NSMutableArray array];
self.tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
[self.navigationController.navigationBar setBarTintColor:[UIColor lightGrayColor]];
}
-(void)filterResults:(NSString *)searchTerm {
myTimer = [NSTimer scheduledTimerWithTimeInterval:3.0
target:self
selector:#selector(queryask)
userInfo:_itemss
repeats:NO];
}
-(void)queryask{
PFQuery *queryCapitalizedString = [PFQuery queryWithClassName:#"Countries"];
[queryCapitalizedString whereKey:#"CountryTitle" containsString:[searchBar.text capitalizedString]];
PFQuery *queryLowerCaseString = [PFQuery queryWithClassName:#"Countries"];
[queryLowerCaseString whereKey:#"CountryTitle" containsString:[searchBar.text lowercaseString]];
PFQuery *querySearchBarString = [PFQuery queryWithClassName:#"Countries"];
[querySearchBarString whereKey:#"CountryTitle" containsString:searchBar.text];
PFQuery *finalQuery = [PFQuery orQueryWithSubqueries:[NSArray arrayWithObjects:queryCapitalizedString,queryLowerCaseString, querySearchBarString,nil]];
[finalQuery findObjectsInBackgroundWithTarget:self selector:#selector(callbackWithResult:error:)];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (void)callbackWithResult:(NSArray *)items error:(NSError *)error
{
if(!error) {
[self.searchResults removeAllObjects];
[self.searchResults addObjectsFromArray:items];
[self.searchDisplayController.searchResultsTableView reloadData];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
return self.itemss.count;
} else {
return self.searchResults.count;
}
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
tableView.rowHeight = 70.0f;
}
- ( UITableViewCell * ) tableView : ( UITableView * ) tableView cellForRowAtIndexPath : ( NSIndexPath * ) indexPath
{
static NSString *uniqueIdentifier = #"MainCell";
CustomCell3 *cell = nil;
cell = (CustomCell3 *) [self.tableView dequeueReusableCellWithIdentifier:uniqueIdentifier];
if (!cell) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MainCell" owner:nil options:nil];
for (id currentObject in topLevelObjects)
{
if([currentObject isKindOfClass:[CustomCell3 class]])
{
cell = (CustomCell3 *)currentObject;
break;
}
}
}
if (tableView == self.tableView) {
cell.MainTitle.text = [[self.itemss objectAtIndex:indexPath.row] objectForKey:#"CountryTitle"];
cell.DescriptTitle.text = [[self.itemss objectAtIndex:indexPath.row] objectForKey:#"DescriptTitle"];
[cell.FlagTitle setImageWithURL:[NSURL URLWithString:[[self.itemss objectAtIndex:indexPath.row] objectForKey:#"ImaURL"]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[ [UIImage imageNamed:#"background.png"] stretchableImageWithLeftCapWidth:0.0 topCapHeight:5.0] ];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[ [UIImage imageNamed:#"background.png"] stretchableImageWithLeftCapWidth:0.0 topCapHeight:5.0] ];
}
if(tableView == self.searchDisplayController.searchResultsTableView) {
PFObject *searchedUser = [self.searchResults objectAtIndex:indexPath.row];
NSString *content = [searchedUser objectForKey:#"CountryTitle"];
NSString *desco = [searchedUser objectForKey:#"DescriptTitle"];
cell.DescriptTitle.text = desco;
cell.MainTitle.text = content;
NSString *image = [searchedUser objectForKey:#"ImaURL"];
[cell.FlagTitle setImageWithURL:[NSURL URLWithString:image]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[ [UIImage imageNamed:#"background.png"] stretchableImageWithLeftCapWidth:0.0 topCapHeight:5.0] ];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[ [UIImage imageNamed:#"background.png"] stretchableImageWithLeftCapWidth:0.0 topCapHeight:5.0] ];
}
return cell;
}
#end
For a way to delay the search, take a look at this SO question:
Delay UISearchbar parsing
A good idea could be to combine this with Logan's suggestion. A search should often not be performed until a minimum of characters have been entered, to avoid too broad searches (not much point performing a search for anything containing "s" in many cases, depending on what you are searching).
Perhaps instead of a timer, you could implement a search bar delegate to run a query after some custom logic, perhaps every 3 letters?
- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if ((searchBar.text.length % 3) == 0) {
[self queryAsk];
}
}
Note: This logic isn't the best, and you might want to explore various parameters for when to search, but I think the concept improves over the timer because it would be more based on input. I think it would give you more control.
in my app I'm using Parse.com as the holder of the data on a database and SearchBarDisplayController for research. I have prepared a following to prepare for my table in order to send the data cells in another view controller and works perfectly ... Now I am having a problem ...
How can I create a "prepareforsegue" for the SearchDisplayController, to activate the cells of the search results?
In here you can see the code for "Prepare for Segue" (or another Method) that I made and the data to be passed ...
Some Ideas on this? Thanks so much!
#import "Ricerca.h"
#import "Custom.h"
#import "DettagliProfilo.h"
#interface Ricerca () <UISearchDisplayDelegate, UISearchBarDelegate>
#property (nonatomic, strong) NSMutableArray *searchResults;
#end
#implementation Ricerca
#synthesize searchResults;
- (void)viewDidLoad {
[super viewDidLoad];
[self loadObjects];
self.searchResults = [NSMutableArray array];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// The className to query on
self.parseClassName = #"_User";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = YES;
self.objectsPerPage = 10;
}
return self;
}
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
//[query whereKeyExists:#"username"];
//[query whereKeyExists:#"email"];
[query orderByAscending:#"username"];
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
return query;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait || UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
UITableViewCell *cell = (UITableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
// Configure the cell
UIColor *color = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.0];
cell.detailTextLabel.backgroundColor = color;
cell.textLabel.backgroundColor = color;
if (tableView == self.tableView) {
cell.textLabel.text = [object objectForKey:#"username"];
cell.detailTextLabel.text = [object objectForKey:#"email"];
cell.imageView.image = [UIImage imageNamed:#"unknown"];
PFFile *imageFile = [object objectForKey:#"foto"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
cell.imageView.image =[UIImage imageWithData:data];
}];
}
else if(tableView == self.searchDisplayController.searchResultsTableView) {
PFObject *searchedUser = [self.searchResults objectAtIndex:indexPath.row];
NSString *content = [searchedUser objectForKey:#"username"];
NSString *email = [searchedUser objectForKey:#"email"];
cell.detailTextLabel.text = email;
cell.textLabel.text = content;
cell.imageView.image = [UIImage imageNamed:#"unknown"];
PFFile *imageFile = [searchedUser objectForKey:#"foto"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
cell.imageView.image =[UIImage imageWithData:data];
}];
NSLog(#"Content: %#", content);
}
return cell;
}
- (void)callbackLoadObjectsFromParse:(NSArray *)result error:(NSError *)error {
if (!error) {
[self.searchResults removeAllObjects];
NSLog(#"Successfully fetched %d entries", result.count);
[self.searchResults addObjectsFromArray:result];
[self.searchDisplayController.searchResultsTableView reloadData];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFUser query];
[query orderByAscending:#"username"];
[query whereKeyExists:#"email"];
[query whereKey:#"username" containsString:searchTerm];
query.cachePolicy = kPFCachePolicyNetworkOnly;
[query findObjectsInBackgroundWithTarget:self selector:#selector(callbackLoadObjectsFromParse:error:)];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
tableView.rowHeight = 80.0f; // or some other height
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
return self.objects.count;
} else {
return self.searchResults.count;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
} else {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Check that a new transition has been requested to the DetailViewController and prepares for it
/*if ([segue.identifier isEqualToString:#"Dettaglio"]){
// Capture the object (e.g. exam) the user has selected from the list
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
PFObject *object = [self.objects objectAtIndex:indexPath.row];
DettagliProfilo *detailViewController = [segue destinationViewController];
detailViewController.Dettaglio = object;*/
if ([[segue identifier]isEqualToString:#"Dettaglio"])
{
DettagliProfilo *sdvc = (DettagliProfilo *)[segue destinationViewController];
if(self.searchDisplayController.active) {
NSIndexPath *indexPath = [[self tableView] indexPathForSelectedRow];
PFObject *object = [self.objects objectAtIndex:indexPath.row];
object = (PFObject *)[[self searchResults]objectAtIndex:[[[[self searchDisplayController]searchResultsTableView]indexPathForSelectedRow]row]];
sdvc.Dettaglio = object;
} else {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
PFObject *object = [self.objects objectAtIndex:indexPath.row];
DettagliProfilo *detailViewController = [segue destinationViewController];
detailViewController.Dettaglio = object;
}
}
}
#end