I am trying to search my objects on parse.com using a uisearchbar and performing 'findObjectsInBackgroundWithBlock'. I am getting the correct results in my output but they are not showing up in my table.
I was previously doing this without blocks, my code worked, it got the correct results but moved very slowly and I was getting a warning, "Warning: A long-running Parse operation is being executed on the main thread"
I had previously been using the code:
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName: #"Items"];
[query whereKeyExists:#"itemName"];
[query whereKeyExists:#"itemDescription"];
[query whereKey:#"tags" containsString:searchTerm];
NSArray *results = [query findObjects];
NSLog(#"%#", results);
NSLog(#"%u", results.count);
[self.searchResults addObjectsFromArray:results];
}
So now I am trying findObjectsInBackgroundWithBlock instead, I have not worked with blocks before so this is where I need help, here is my new code:
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName: #"Items"];
[query whereKey:#"tags" containsString:searchTerm];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog(#"%#", objects);
NSLog(#"%u", objects.count);
[self.searchResults addObjectsFromArray:objects];}];
Here is some more of my code
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
return self.objects.count;
} else {
return self.searchResults.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
NSString *uniqueIdentifier = #"cell";
HomeCell *cell = nil;
cell = (HomeCell *) [self.tableView dequeueReusableCellWithIdentifier:uniqueIdentifier];
if (!cell) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"HomeCell" owner:nil options:nil];
for (id currentObject in topLevelObjects)
{
if([currentObject isKindOfClass:[HomeCell class]])
{
cell = (HomeCell *)currentObject;
break;
}
}
}
if (tableView != self.searchDisplayController.searchResultsTableView) {
NSString *itemName = [object objectForKey:#"itemName"];
NSString *itemDescription = [object objectForKey:#"itemDescription"];
//cell.textLabel.text = last;
cell.cellTitleLabel.text = itemName;
cell.descriptionLabel.text = itemDescription;
cell.priceLabel.text = [object objectForKey:#"price"];
PFFile *thumbnail = [object objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = cell.imageFile;
thumbnailImageView.image = [UIImage imageNamed:#"Facebook #2x.png"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
}
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
PFObject *obj2 = [self.searchResults objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"Items"];
PFObject *searchedItems = [query getObjectWithId:obj2.objectId];
NSString *itemName = [searchedItems objectForKey:#"itemName"];
NSString *itemDescription = [searchedItems objectForKey:#"itemDescription"];
cell.cellTitleLabel.text = itemName;
cell.descriptionLabel.text = itemDescription;
cell.priceLabel.text = [searchedItems objectForKey:#"itemName"];
PFFile *thumbnail = [searchedItems objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = cell.imageFile;
thumbnailImageView.image = [UIImage imageNamed:#"Facebook #2x.png"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
}
return cell;
Any help would be greatly appreciated,
cheers
In order to update the table view, you need to call the reloadData: method once you have added the new search results. Make sure that you call this method within the block that you provide to findObjectsInBackgroundWithBlock: because this block of code will be run on a separate thread. This causes the method to return instantly, and code after this method will then run before the block has actually executed. Your find objects code within filterResults: should look something like this:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// This block is called from a background thread once the query has been executed
NSLog(#"%#", objects);
NSLog(#"%u", objects.count);
[self.searchResults addObjectsFromArray:objects];
// Refresh the table view on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
[self.searchDisplayController.searchResultsTableView reloadData];
});
}];
Differentiate from your search table view and your regular tableview when you create the cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
if (tableView = self.tableView) {
//Configure your cell normally
}
else {
//Configure your cells using
cell.someAttribute = self.searchResults[indexPath.row].someAttribute;
}
Related
I am working on a app that you can change when a item on the menu is in stock or out of stock.
I have it now so it changes the UISwitch to on or off when it loads the screen. I need each switch to change a NSString in parse that makes it one or zero.One meaning that it is on zero meaning its off.
I am fairly new to objective c and parse so if any one could help me get a start on this problem that would be great!
You might use something like that:
PFQuery *query = [PFQuery queryWithClassName:#"YourClass"];
[query whereKey:#"user" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject * yourClass, NSError *error) {
if (!error) {
// Found yourClass object
[yourClass setObject:isInStock forKey:#"isInStock"];
// Save
[yourClass saveInBackground];
} else {
// Did not find any yourClass object for the current user
NSLog(#"Error: %#", error);
}
}];
NSArray *listObjects = .... (loading from Server) // List of PFObject
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
PFObject *object = [listObjects objectAtIndex:indexPath.row];
YourCell *cell = .....
if ([[object valueForKey:#"sandwichesOutofstock"] intValue] == 1)
cell.switch.on = true;
else
cell.switch.on = false;
cell.switch.tag = 500 + index.row;
[cell.switch addTarget:self action:#selector(switchTouch:) forControlEvents:UIControlEventTouchUpInside]
.........
}
(IBAction)switchTouch:(UISwitch *)switch{
long index = switch.tag - 500;
PFObject *object = [listObjects objectAtIndex:index];
if(switch.on)
[object setValue:#"1" ForKey:#"sandwichesOutofstock"];
else{
[object setValue:#"0" ForKey:#"sandwichesOutofstock"];
}
[object saveInBackground];
[self.tableView reloadRowsAtIndexPaths:[NSIndexPath indexPathForRow:index inSection:0] withRowAnimation:UITableViewRowAnimationNone];
}
You could assign a reference of the PFObject to the cell. Then when the switch changes just get the cell's object and make the change.
I'm trying to do a search function in my table view, to return objects from my Parse.com class.
I'm getting this error when I try to make a search in the UISearchBar:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier FeaturedTableViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
Here's how I'm doing it:
#interface DiscoverViewController () <UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, retain) PFQuery *query;
#property (nonatomic, retain) PFObject *category;
#end
#implementation DailyDiscoverViewController
- (void)viewDidLoad {
[super viewDidLoad];
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];
_categories = [[NSMutableArray alloc] initWithCapacity:100];
}
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"Projects"];
[query whereKeyExists:#"name"];
[query whereKey:#"name" containsString:searchTerm];
NSArray *results = [query findObjects];
[self.searchResults addObjectsFromArray:results];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)refresh {
PFQuery *query = [PFQuery queryWithClassName:#"Categories"];
[query orderByDescending:#"name"];
[query findObjectsInBackgroundWithBlock:^(NSArray *posts, NSError *error) {
if (!error) {
//NSLog(#"%#", posts);
} else {
}
[_categories setArray:posts];
[self.tableView reloadData];
[_loadingView stopAnimating];
[_refreshControl endRefreshing];
}];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
if (section == 0) {
return 1;
} else {
return self.categories.count;
}
} else {
return self.searchResults.count;
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
return 320;
} else {
return 52;
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
_featuredObject = [_featured objectAtIndex:indexPath.row];
DiscoverFeaturedTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DiscoverFeaturedTableViewCell" forIndexPath:indexPath];
[(PFFile*)_featuredObject[#"picture"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
cell.image1.image = [UIImage imageWithData:data];
}];
return cell;
} else {
_category = [_categories objectAtIndex:indexPath.row];
DiscoverTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DiscoverTableViewCell" forIndexPath:indexPath];
cell.name.text = _category[#"name"];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
PFUser *obj2 = [self.searchResults objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"Projects"];
PFObject *searchedUser = [query getObjectWithId:obj2.objectId];
NSString *first = [searchedUser objectForKey:#"name"];
cell.name.text = [first substringToIndex:1];
cell.name.text = first;
return cell;
}
return cell;
}
}
#end
I think you can look this stack over flow post : POST
K.
Ok,
Try it on viewDidLoad:
YourCustomCell *yourCustomCell = [UINib
nibWithNibName:#"YourCustomCell" bundle:nil]; [self.tableView
registerNib:cellNib forCellReuseIdentifier:#"cell"];
Note: Your custom cell must have a .xib of course.
You could try this, I remember running into this exact issue and this was one of the solutions I tried:
[self.tableView registerClass:[DiscoverTableViewCell class] forCellReuseIdentifier:#"DiscoverTableViewCell"];
However, I distinctly remember that this didn't work. It turned out I hadn't correctly connected the cells in the storyboard. Furthermore, make sure that the class of your storyboard cell has been changed to represent your cell's custom class.
I have a friendslist view controller that allows the user to see pending friend requests. The isPending method is first used to see if a particular user has a pending friend request with the current user.
-(BOOL)isPending:(PFUser *)user
{
for (PFUser *pending in self.Pending){
if ([pending.objectId isEqualToString:user.objectId]){
return YES;
}
}
return NO;
}
This method is called within cellForRowAtIndexPath method inside the TableViewController. THe problem that I am experiencing is that If I run the query inside the tableViewController users who are pending are properly displayed. When I moved the query to a singleton datasource class, I have setup a delegate method that is called when data is returned from the query.
-(void)pendingFriendsQuarryDidFinishWithData{
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsList];
[self.tableView reloadData];
}
Calling reloadData does not cause the checkmarks to appear next to the users names.
EDIT: Here is the code for cellForRowAtIndexPath. It does not change with the singleton.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
PFUser *user = [self.allUsers objectAtIndex:indexPath.row];
cell.textLabel.text = user.username;
cell.detailTextLabel.text = user.email;
cell.imageView.image = [UIImage imageNamed:#"Treehouse.png"];
if([self isPending:user]){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
return cell;
}
What does change is how the data is obtained. Without the singleton here is the code
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/*
self.currentUser = [PFUser currentUser];
if (self.currentUser != nil){
PFQuery *userQuery = [PFUser query];
PFQuery *pendingUser = [PFUser query];
PFRelation *friendRelation = [self.currentUser relationForKey:#"friendRelation"];
PFQuery *existingFriends = [friendRelation query];
PFQuery *pendingFriends = [PFQuery queryWithClassName:#"FriendRequest"];
userQuery.cachePolicy = kPFCachePolicyCacheThenNetwork;
pendingFriends.cachePolicy = kPFCachePolicyCacheThenNetwork;
[pendingFriends whereKey:#"fromUser" equalTo:self.currentUser.objectId];
[pendingFriends whereKey:#"status" equalTo:#"Pending"];
[userQuery whereKey:#"objectId"doesNotMatchKey:#"toUser" inQuery:pendingFriends];
[userQuery whereKey:#"objectId" doesNotMatchKey:#"objectId" inQuery:existingFriends];
[userQuery orderByAscending:#"username"];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#" Error %# %#", error, [error userInfo] );
}else{
NSLog(#"All Users Array Starts Here: %#", objects);
self.allUsers = objects;
[self.tableView reloadData];
}
}];
[pendingUser whereKey:#"objectId" matchesKey:#"toUser" inQuery:pendingFriends];
[pendingUser orderByAscending:#"username"];
[pendingUser findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#"%# %#", error, [error userInfo]);
}else{
[self.Pending addObjectsFromArray:objects];
NSLog(#"Pending Friends Array Stars here: %#", self.Pending);
}
}];
}
*/
}
With singleton
- (void)viewDidLoad
{
dataHolder = [DataHolder sharedInstance];
dataHolder.delegate = self;
self.friends = [NSMutableArray new];
self.allUsers = [NSMutableArray new];
self.allUsers = [NSMutableArray arrayWithArray:dataHolder.getAllUsersWithQuarry];
self.Pending = [NSMutableArray new];
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsListWithQuarry];
[super viewDidLoad];
NSLog(#"Now in Connection Editor");
}
-(void)allUsersQuarryDidFinishWithData{
self.allUsers = [NSMutableArray arrayWithArray:dataHolder.getAllUsers];
[self.tableView reloadData];
}
-(void)pendingFriendsQuarryDidFinishWithData{
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsList];
[self.tableView reloadData];
}
Silly question, did you set the datasource of your tableviewcontroller to point to your singleton datasource?
tableview.datasource = [your singleton class instance]
and make sure that you step into it in the debugger.
Hope this helps.
I am tying to display in my table view an array that is on a database on parse.com
I can display the first item in the array, but not the rest.
- (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];
}
_ListArray = [object objectForKey:#"content"];
NSLog(#"OBJECT = %#",_ListArray);
NSString *cellValue = [NSString stringWithFormat:#"%#", [_ListArray objectAtIndex:indexPath.row]];
cell.textLabel.text = cellValue;
if(refresh <counter)
{
NSString *br = #"<br>";
List = [NSString stringWithFormat:#"%#%#%#", List, br, cell.textLabel.text];
NSLog(#"%#",List);
refresh ++;
}
cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
If I try to use:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_ListArray count];
}
I get nothing in the table.
edit update:
did remove the self.ListArray and going with _ListArray.
in the h file:
NSMutableArray *ListArray;
...
#property (retain, atomic) NSMutableArray *ListArray;
and then in m file:
#synthesize ListArray = _ListArray;
regarding the:
self.ListArray = [object objectForKey:#"content"];
the object is the PFObject.
i have queryForTable where i get the data from parse.
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:#"List"];
[query whereKey:#"user" equalTo:[PFUser currentUser]]; // user
[query whereKey:#"objectId" equalTo:[theListID objectId]]; // the specific list
counter =[query countObjects];
NSLog(#"%d",counter);
// If Pull To Refresh is enabled, query against the network by default.
if (self.pullToRefreshEnabled) {
query.cachePolicy = kPFCachePolicyNetworkOnly;
}
// 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;
}
[query orderByDescending:#"createdAt"];
return query;
}
And to the objectForKey:#"content"];
content is the name of the column in my class on parse
so i need to say what object from the class i want to get, and that is the array column named "content"
The logg, (when the NSLog is active on the array.
Inköpslista[7856:12e03] Warning: A long-running Parse operation is being executed on the main thread.
Break on warnParseOperationOnMainThread() to debug.
2013-10-21 14:23:31.008 Inköpslista[7856:12e03] 1
2013-10-21 14:23:31.017 Inköpslista[7856:12e03] OBJECT = (
"Br\U00f6d",
Anka,
Boll,
Sko
)
2013-10-21 14:23:31.017 Inköpslista[7856:12e03] <html><body><br>Bröd
2013-10-21 14:23:31.545 Inköpslista[7856:12e03] OBJECT = (
"Br\U00f6d",
Anka,
Boll,
Sko
)
Got it to work, did put the array were i get the data from parse, and then only call
cell.textLabel.text = [_ListArray objectAtIndex:indexPath.row];
and then i can use:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_ListArray count];
}
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.