UISearchDisplayController Text Did Change Lagging Behind - ios

I am trying to search through a list of users in my Parse database. To do so I have a search bar controller and table view. When a user is searching, it seems like the search results are a letter behind. For example if I search "Be" it will show all the names starting with "B" instead of "Be" and when I search "Ben" it shows all the users starting with "Be".
Here is my textDidChange Method:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
NSString *string = searchText;
string = string.lowercaseString;
if(string.length>0){
PFUser *currentUser = [PFUser currentUser];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"name_lower" containsString:string];
[query whereKey:#"username" notEqualTo:currentUser.username];
[query orderByAscending:#"name_lower"];
[query setLimit:1000];
[query findObjectsInBackgroundWithBlock:^(NSArray *array,NSError *error){
if(!error){
results = [[NSArray alloc]initWithArray:array];
[_mainTableView reloadData];
}else{
[ProgressHUD showError:#"Error Searching"];
}
}];
}else{
NSLog(#"NO RESULTS");
results = nil;
[_mainTableView reloadData];
}
}
Then in my cellforrow:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
PFUser *user = results[indexPath.row];
[user fetchIfNeeded];
cell.textLabel.text = [NSString stringWithFormat:#"%# - #%#",user[#"name"],user[#"username"]];
cell.detailTextLabel.text = user[#"university"];
}

Hey try to put constraints like this:
[query whereKey:#"name_lower" hasPrefix:string];
instead of
[query whereKey:#"name_lower" containsString:string];
After all I would do it this way:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
NSString *string = searchText;
string = string.lowercaseString;
if(string.length>0){
PFQuery *query = [PFUser query];
[query whereKey:#"name_lower" hasPrefix:string];
[query whereKey:#"username" notEqualTo:[[PFUser currentUser] username]];
[query orderByAscending:#"name_lower"];
[query setLimit:1000];
[query findObjectsInBackgroundWithBlock:^(NSArray *array,NSError *error){
if(!error){
results = array;
[_mainTableView reloadData];
} else {
[ProgressHUD showError:#"Error Searching"];
}
}];
} else {
NSLog(#"NO RESULTS");
results = nil;
[_mainTableView reloadData];
}
}

Related

How can I show a PFUser array of Parse in a UITableVIew in iOS?

I have tried following code so far:
PFGeoPoint * myGeoPoint = [PFUser currentUser][#"coordinates"]; // Your geoPoint
PFQuery *query = [PFUser query];
[query includeKey:#"User"];
[query whereKey:#"coordinates" nearGeoPoint:myGeoPoint withinMiles:radiusInMiles];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error){
for (PFUser *object in objects){
users = (NSArray*)[object ObjectForKey:#"User"];
}
[self.tableView reloadData];
}
}
}];
- (PFUser*) objectAtIndexPath:(NSIndexPath*)indexPath {
PFUser *object = [PFUser objectWithClassName: #"User"];
[object setObject:[users objectAtIndex:indexPath.row] forKey:#"User"];
return object;
}
// Use myObjects for numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section {
return users.count;
}
//Use your custom object for cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath object:(PFUser *)object {
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// Configure the cell using object
UILabel *reviewLabel = (UILabel *)[cell viewWithTag:10];
reviewLabel.text = [object objectForKey:object.username];
PFFile *userImageFile = object[#"profilePic"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
if (imageData != nil)
cell.imageView.image = [UIImage imageWithData:imageData];
else cell.imageView.image= [UIImage imageNamed:#"defaultPerson"];
}
}];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SMChatViewController *chatController = [SMChatViewController alloc];
// [chatController initWithPhoto:image];
[self presentModalViewController:chatController animated:YES];
}
In this code I am retrieving an array of PFUsers as "objects".When I am saving the objects array to users it returns as empty. So can anyone please suggest me something that How can I set the username and profilepic in a tableView?
I guess query is not correct.
Try this:
PFGeoPoint * myGeoPoint = [PFUser currentUser][#"coordinates"]; // Your geoPoint
PFQuery *query = [PFQuery queryWithObject:#"_User"];
NSArray *userList = [NSArray new];
[query whereKey:#"coordinates" nearGeoPoint:myGeoPoint withinMiles:radiusInMiles];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error){
userList = objects;
[self.tableView reloadData];
}
}];
In your code you're asking 'User' objects, then you fetching 'User' field from this objects.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
PFUser *user = (PFUser*)[users objectAtIndex:indexPath.row];
cell.textLabel.text=user.username;
PFFile *userImageFile = user[#"profilePic"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
if (imageData != nil)
cell.imageView.image = [UIImage imageWithData:imageData];
else cell.imageView.image= [UIImage imageNamed:#"defaultPerson.png"];
}
}];
return cell;
}

Having trouble calling textFieldShouldReturn from a UITextField within a custom UITableCell

I'm trying to call textFieldShouldReturn on a UITextField within a custom UITableCell I have on my table view. I use this bit of code to grab the index path of the cell, I found this method on stackoverflow in another post:
NSIndexPath *indexPath = [self.tableView indexPathForCell:(friendTableCell*)[[textField superview] superview]];
I don't have any errors, when I compile and run, but when I select a cell, enter text, and try to hit return on the keyboard, nothing happens. I have excluded the rest of the code that should be called because it is rather lengthy, and I have tested in didSelectRowAtIndexPath and it works. I can include it if someone really cares to see though. I thought that perhaps my delegate wasn't set properly for the keyboard, but I have include UITextFieldDelegate in my header, and I'm not really sure how else to set the delegate for the keyboard seeing as I'm accessing a custom cell within a tableview. If someone has an idea of what to do, or an alternative method, I would really appreciate it. Thanks!
UPDATE - Complete textFieldShouldReturn method
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:(friendTableCell*)[[[textField superview] superview] superview]];
NSString *sectionTitle = [self.usernameSectionTitles objectAtIndex:indexPath.section];
NSArray *sectionUser = [self.sortedUsernames objectForKey:sectionTitle];
self.recipient = [sectionUser objectAtIndex:indexPath.row];
friendTableCell *cell = (friendTableCell *)[self.tableView cellForRowAtIndexPath:indexPath];
NSString *sendingUser = self.currentUser.username;
NSString *message = [NSString stringWithFormat:#"from %#", [sendingUser uppercaseString]];
if ([self.locationSwitch isOn])
{
if ([cell.message.text length] > 0)
{
message = [NSString stringWithFormat:#"from %# %#", [sendingUser uppercaseString],cell.message.text];
}
CLLocation *location = locationManager.location;
if(!location)
{
[cell closeMessage:self];
}
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
CLLocationCoordinate2D coordinate = [location coordinate];
PFGeoPoint *geoPoint = [PFGeoPoint geoPointWithLatitude:coordinate.latitude longitude:coordinate.longitude];
PFObject *object = [PFObject objectWithClassName:#"Location"];
[object setObject:geoPoint forKey:#"location"];
[object setObject:self.recipient.objectId forKey:#"recipient"];
[object saveEventually:^(BOOL succeeded, NSError *error) {
if(succeeded){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Your Location was sent!"message:[error.userInfo objectForKey:#"error"] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
dispatch_sync(queue, ^{
PFQuery *locationQuery = [PFQuery queryWithClassName:#"Location"];
[locationQuery whereKey:#"recipient" equalTo:self.recipient.objectId];
/****** CHANGE THE getFirstObjectInBackground method so it doesn't take the first each time
or DELETE THE LOCATION after it has been viewed **************/
[locationQuery getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
else {
self.locationId = object.objectId;
NSLog(#"Here is you location ID: %#", self.locationId);
dispatch_sync(queue, ^{
self.data = #{
#"alert":message,
#"locationId":self.locationId
};
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:#"objectId" equalTo:self.recipient.objectId];
PFQuery *query = [PFInstallation query];
[query whereKey:#"currentUser" matchesQuery:userQuery];
PFPush *push= [[PFPush alloc]init];
[push setQuery:query];
[push setData:self.data];
[push sendPushInBackground];
});
}
}];
});
}
}];
});
}
else if ([cell.message.text length] > 0)
{
message = [NSString stringWithFormat:#"from %# %#", [sendingUser uppercaseString],cell.message.text];
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:#"objectId" equalTo:self.recipient.objectId];
PFQuery *query = [PFInstallation query];
[query whereKey:#"currentUser" matchesQuery:userQuery];
PFPush *push= [[PFPush alloc]init];
[push setQuery:query];
[push setMessage:message];
[push sendPushInBackground];
}
else
{
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:#"objectId" equalTo:self.recipient.objectId];
PFQuery *query = [PFInstallation query];
[query whereKey:#"currentUser" matchesQuery:userQuery];
PFPush *push= [[PFPush alloc]init];
[push setQuery:query];
[push setMessage:message];
[push sendPushInBackground];
}
NSLog(#"Message sent!");
[cell closeMessage:self];
[cell resignFirstResponder];
return YES;
}
Try this NSIndexPath *indexPath = [self.tableView indexPathForCell:(friendTableCell*)[[[textField superview] superview] superview]]; as there is another scrollview layer in between container view and cell .
Also i can see you have written [cell resignFirstResponder]; instead you should use [textField resignResponder]
Hope this would work..

how to put an Array of images into specific UICollectionViewCells

I'm currently developing a App thats home view controller shows a display of all the users friends, their names and their profile images (if they have one), each within a cell of a UICollectionView. I am using tags to identify the UI elements within each cell and Parse as the backend. Each user and their relations are stored under the class "User" and then their profile images (if they have chosen one/taken one) are stored under another class called "UserImage". I've managed to set their usernames to the cells but having difficulty with the images. Basically each cell is downloading the entire image array, which I believe is causing the app to crash with this error. Also as soon as the user adds a friend without a profile picture the app seems to act strange..
[__NSArrayM objectAtIndex:]: index 8 beyond bounds [0 .. 1]
here is how I save the image to parse.. This is in the users profile view controller.
-(void)uploadImage {
NSData *fileData;
NSString *fileName;
NSString *fileType;
UIImage *newImage = [self scaleImage:self.image toSize:CGSizeMake(340.0,340.0)];
fileData = UIImagePNGRepresentation(newImage);
fileName = #"profileimage.png";
fileType = #"profileimage";
PFFile *file = [PFFile fileWithName:fileName data:fileData];
[file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
PFUser *user = [PFUser currentUser];
PFObject *userImage = [PFObject objectWithClassName:#"UserImage"];
[userImage setObject:file forKey:#"file"];
[userImage setObject:fileType forKey:#"fileType"];
[userImage setObject:user forKey:#"user"];
[userImage saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(#"Error saving image");
}
else {
NSLog(#"Image Saved");
}
}];
}];
}
I then query for the current users relations within the viewWillAppear: method of the HomeViewController.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController.navigationBar setHidden:NO];
self.friendsRelation = [[PFUser currentUser] objectForKey:#"friendsRelation"];
self.friends = [[NSMutableArray alloc] init];
PFQuery *query = [self.friendsRelation query];
[query orderByAscending:#"username"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error %# %#", error, [error userInfo]);
}
else {
[self.friends addObjectsFromArray:objects];
[self.collectionView reloadData];
}
}];
}
Setting the amount of cells to the amount of the users friends..
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [self.friends count];
}
And finally setting up the cells within the cellForItemAtIndexPath: method
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
PFUser *user = [self.friends objectAtIndex:indexPath.row];
if (cell.tag == 0) {
UILabel *cellLabel = (UILabel *)[cell viewWithTag:25];
cellLabel.text = user.username;
PFImageView *homeImageView = (PFImageView *)[cell viewWithTag:50];
[homeImageView setContentMode:UIViewContentModeScaleAspectFill];
homeImageView.clipsToBounds = YES;
homeImageView.layer.cornerRadius = 7;
self.friendImages = [[NSMutableArray alloc] init];
PFQuery *imageQuery = [PFQuery queryWithClassName:#"UserImage"];
[imageQuery whereKey:#"user" containedIn:self.friends];
[imageQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
//need a statement that defines whether a friend has an image or not, if they do then load that image into their cell, if not then set default image
[self.friendImages addObjectsFromArray:objects];
PFObject *imageObject = [self.friendImages objectAtIndex:indexPath.row];
PFFile *imageFile = [imageObject objectForKey:#"file"];
homeImageView.file = imageFile;
[homeImageView loadInBackground];
NSLog(#"Retrieved %d images", [self.friendImages count]);
}
else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
return cell;
}
Any help would be appreciated! keep in mind I'm only a couple of weeks into learning iOS..

Parse loading Image into UIImageView iOS

Here is the code for the data I am passing on to the scene with the image:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
rowNo = indexPath.row;
PFUser *currentUser = [PFUser currentUser];
PFQuery *query = [PFQuery queryWithClassName:#"Photo"];
[query whereKey:#"username" equalTo:currentUser.username];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
_photo = [objects objectAtIndex:rowNo];
}];
[self performSegueWithIdentifier:#"showImageCloseup" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
ImageCloseupViewController *destination = [segue destinationViewController];
destination.photoObject = _photo;
}
And here is the code which loads the image:
- (void)viewDidLoad
{
[super viewDidLoad];
PFFile *p = [_photoObject objectForKey:#"photo"];
[p getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if(!error){
UIImage *image = [UIImage imageWithData:data];
[_imageView setImage:image];
}
}];
For some reason, the image is not loading, why is this is so? How can I fix it?
This:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
_photo = [objects objectAtIndex:rowNo];
}];
[self performSegueWithIdentifier:#"showImageCloseup" sender:self];
Needs to be:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
_photo = [objects objectAtIndex:rowNo];
[self performSegueWithIdentifier:#"showImageCloseup" sender:self];
}];
Because:
// Runs 1st - executes in background thread stores block, then runs block once it's done
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// Runs 3rd - already segued at this point
_photo = [objects objectAtIndex:rowNo];
}];
// Runs 2nd - starts segue
[self performSegueWithIdentifier:#"showImageCloseup" sender:self];
However, this seems like there's an overall problem with your design pattern. If you already have access to objects, you shouldn't have to requery the entire database each time. Do you have a reference to the array populating the tableView? If so, something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
rowNo = indexPath.row;
_photo = yourDataArray[indexPath.row];
[self performSegueWithIdentifier:#"showImageCloseup" sender:self];
}

How to search PFObject UITableView

Im trying to figure out how to search/Query a PFObject and not a user. This is what I have to so far and its finding but not displaying. I found this post and followed it but did not come to a running result because it does not show results. I cant figure out how to show the PFObject so thats what I need help with:)
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"New"];
[query whereKeyExists:#"by"]; //this is based on whatever query you are trying to accomplish
[query whereKeyExists:#"title"]; //this is based on whatever query you are trying to accomplish
[query whereKey:#"title" containsString:searchTerm];
NSArray *results = [query findObjects];
NSLog(#"%#", results);
// NSLog(#"%u", results.count);
[self.searchResults addObjectsFromArray:results];
}
Then I am trying to display here:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [object objectForKey:self.textKey];
if (tableView != self.searchDisplayController.searchResultsTableView) {
/* PFObject *title = [PFQuery queryWithClassName:#"title"];
PFQuery *query = [PFQuery queryWithClassName:#"New"];
PFObject *searchedUser = [query getObjectWithId:title.objectId]; NSString * usernameString = [searchedUser objectForKey:#"title"]; cell.textLabel.text = [NSString stringWithFormat:#"%#", usernameString];
*/
PFQuery *query = [PFQuery queryWithClassName:#"New"];
[query whereKey:#"title" equalTo:#"by"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d title", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
PFQuery *query = [PFQuery queryWithClassName:#"New"];
[query whereKey:#"title" equalTo:#"by"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d title", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
/* //PFObject *obj2 = [self.searchResults objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"New"];
PFObject *searchedUser = [query getObjectWithId:obj2.objectId];
NSString *first = [searchedUser objectForKey:#"title"];
NSString *last = [searchedUser objectForKey:#"by"];
cell.textLabel.text = [first substringToIndex:1];
NSString *subscript = last;
// cell.categoryName.text = [searchedUser objectForKey:#"category"];
*/
}
return cell;
}
What your code is doing is that first, in filterResults, you are retrieving all objects that contain the field "by" and the field "title" and where "title" contains searchTerm.
THEN, when you create a cell, you do another query (which you should never do, as it now has a long-lasting operation to do for each and every cell being displayed. Scrolling this view would never work. And besides; you are comparing the title with the string #"by", which I am pretty sure is not your intention.
So, your main problem is in they way you build your query, and your secondary problem is that you're doing this for every cell.
What you need to do is get all the data you want on the first search, and then iterate through those data in an array when displaying.
Something like:
- (void)viewDidLoad {
PFQuery *query = [PFQuery queryWithClassName:#"TestClass"];
query.cachePolicy = kPFCachePolicyNetworkOnly;
query.limit = 50;
[query whereKey:#"city" equalTo:chosenCity];
[query whereKey:#"sex" equalTo:#"female"];
[query findObjectsInBackgroundWithTarget:self selector:#selector(callbackLoadObjectsFromParse:)];
- (void)callbackLoadObjectsFromParse:(NSArray *)result error:(NSError *)error {
if (!error) {
NSLog(#"Successfully fetched %d entries", result.count);
self.allTestObjects = result;
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}
Then, later in cellForRowAtIndexPath you use this array (no more queries) as the datasource for your data:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFObject * testObject = [self.allTestObjects objectAtIndex:indexPath.row],
cell.textLabel.text = [testObject objectForKey:#"name"];
return cell;
}
When working with databases, never do lookups in tableviewcells. Prepare your data first, and then present them with top performance.

Resources