Autocomplete text field in iOS not working - ios

i am newbie in iOS development, i want to add autocomplete textfield in my ap i write a code for that like as
- (void)viewDidLoad
{
[self get data];
}
-(void)getdata
{
NSMutableArray *allObjects = [NSMutableArray array];
NSUInteger limit = 1000;
__block NSUInteger skip = 0;
PFQuery *query = [PFQuery queryWithClassName:#"MapInfo"];
[query setLimit: limit];
[query setSkip: skip];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
[allObjects addObjectsFromArray:objects];
if (objects.count == limit) {
skip += limit;
[query setSkip: skip];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
[allObjects addObjectsFromArray:objects];
self.qpinname=[allObjects valueForKey:#"GPIN"];
self.locationarray=[allObjects valueForKey:#"Location"];
self.latitude=[self.locationarray valueForKey:#"lat"];
self.longitude=[self.locationarray valueForKey:#"lng"];
self.address=[allObjects valueForKey:#"Address"];
NSLog(#"Address %#",self.address);
self.usernameArray=[allObjects valueForKey:#"AddedBy"];
}];
}
}
else
{
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
then i got my data array and i want to show it on my table view like as
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring
{
[self.autoaddress removeAllObjects];
for(NSString *curString in self.address)
{
NSRange substringRange = [curString rangeOfString:substring];
if (substringRange.location == 0) {
[self.autoaddress addObject:curString];
}
}
[_autocompleteTableView reloadData];
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
_autocompleteTableView.hidden = NO;
NSString *substring = [NSString stringWithString:textField.text];
substring = [substring stringByReplacingCharactersInRange:range withString:string];
[self searchAutocompleteEntriesWithSubstring:substring];
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section
{
return self.autoaddress.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
static NSString *AutoCompleteRowIdentifier = #"AutoCompleteRowIdentifier";
cell = [tableView dequeueReusableCellWithIdentifier:AutoCompleteRowIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:AutoCompleteRowIdentifier] ;
}
cell.textLabel.text = [self.autoaddress objectAtIndex:indexPath.row];
return cell;
}
then i got error in code
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring
{
[self.autoaddress removeAllObjects];
for(NSString *curString in self.address)
{
NSRange substringRange = [curString rangeOfString:substring];
if (substringRange.location == 0) {
[self.autoaddress addObject:curString];
}
}
[_autocompleteTableView reloadData];
}
Here i got error in line NSRange substringRange = [curString rangeOfString:substring]; -[NSNull rangeOfString:]: unrecognized selector sent to instance i find it in google but i not get solution please help me for this
thanks.

Your self.address property is probably NSNull or it contains NSNull if it is an array. You can check if object is NSNull the following way:
if(![object isEqual:[NSNull null]])
{
//do something if object is not equals to [NSNull null]
}
The code is from this answer.
EDIT: If you use NSMutableArray you can call the method [self.address removeObjectIdenticalTo:[NSNull null]] to remove all NSNull objects from the array after self.address=[allObjects valueForKey:#"Address"];

Related

Application Termination because of NSRangeException (NSMutableArray)

I'm trying to retrieve uploaded thumbnailPhotos from Parse to display them in UITableViewCells, but I get an exception thrown in everytime. The error code is as follows: "Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'"
This is my code:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
PFQuery *query = [PFQuery queryWithClassName:#"Events"];
[query orderByDescending:#"date"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
} else {
self.events = (NSMutableArray *) objects;
[self.tableView reloadData];
for (PFObject *event in self.events) {
NSInteger index = [self.events indexOfObject:event];
PFFile *imageFile = [event objectForKey:#"thumbnailImage"];
[imageFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) {
if (error) {
//Handle Error
} else {
UIImage *image = [UIImage imageWithData:result];
if (self.thumbnailPhotos == nil) {
self.thumbnailPhotos = [NSMutableArray array];
self.thumbnailPhotos[index] = image;
} else {
self.thumbnailPhotos[index] = image;
}
[self.tableView reloadData];
}
}];
}
}
}];
}
CellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *reuseIdentifier = #"Cell";
EventsTableViewCell *cell = (EventsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];
PFObject *event = [self.events objectAtIndex:indexPath.row];
NSDate *date = [event objectForKey:#"date"];
NSString *dateString = [self.dateFormat stringFromDate:date];
NSString *timeString = [self.timeFormat stringFromDate:date];
NSLog(#"IndexPath.row = %ld", (long)indexPath.row);
if ([self.thumbnailPhotos objectAtIndex:indexPath.row] != nil) {
cell.imageView.image = self.thumbnailPhotos[indexPath.row];
} else {
NSLog(#"Nil, Application will crash!");
}
cell.eventNameLabel.text = [event objectForKey:#"title"];
cell.dateLabel.text = dateString;
cell.timeLabel.text = timeString;
[cell.timeLabel sizeToFit];
return cell;
}`
I had to add the index value of self.events because the thumbnailPhotos were downloaded in different speed, so my Cells always showed the wrong photo for the wrong event.
I hope this was enough details to figure the problem out.
Application is crashing because thumbnailPhotos doesn't have any object at index for assignment. Please use following code.
Updated Code which will support Dictionary to hold thumbnail images
/*
Define events as NSMutableArray which holds all events
Define thumbnailPhotos as NSMutableDictionary which holds thumbnail image for index as key
*/
//Create a weak Reference of self for accessing self within block
__weak __typeof(self)weakSelf = self;
PFQuery *query = [PFQuery queryWithClassName:#"Events"];
[query orderByDescending:#"date"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
} else {
//Create a strong reference for updating the UI
__strong __typeof(weakSelf)strongSelf = weakSelf;
//Assign the events to instance object
strongSelf.events = [NSMutableArray arrayWithArray:objects];
//Alloced thumbnail dictionary
strongSelf.thumbnailPhotos = [NSMutableDictionary dictionary];
//Reload tableView so that data will be visible
[strongSelf.tableView reloadData];
for (PFObject *event in strongSelf.events) {
//Define index as block type because we have to use this instance within block
__block NSInteger index = [strongSelf.events indexOfObject:event];
PFFile *imageFile = [event objectForKey:#"thumbnailImage"];
[imageFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) {
if (error) {
//Handle Error
} else {
UIImage *image = [UIImage imageWithData:result];
//Set the image against index
[strongSelf.thumbnailPhotos setObject:#"" forKey:#(index)];
//Reload only cell for which image is just downloaded
[strongSelf.tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}];
}
}
}];
Updated:
Modify your cellForRowAtIndexPath as below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *reuseIdentifier = #"Cell";
EventsTableViewCell *cell = (EventsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];
PFObject *event = [self.events objectAtIndex:indexPath.row];
NSDate *date = [event objectForKey:#"date"];
NSString *dateString = [self.dateFormat stringFromDate:date];
NSString *timeString = [self.timeFormat stringFromDate:date];
NSLog(#"IndexPath.row = %ld", (long)indexPath.row);
if ([self.thumbnailPhotos valueForKey:#(indexPath.row)]) {
cell.imageView.image = [self.thumbnailPhotos valueForKey:#(indexPath.row)];
} else {
NSLog(#"Nil, Application will crash!");
}
cell.eventNameLabel.text = [event objectForKey:#"title"];
cell.dateLabel.text = dateString;
cell.timeLabel.text = timeString;
[cell.timeLabel sizeToFit];
return cell;
}
In this implementation thumbnail Image is getting saved in to dictionary and second reload of tableView is per cell basis instead of reloading complete tableView for single thumbnail download.

setObjectForKey: object cannot be nil when returning objects from Parse.com

I am returning a number of objects from my Parse class to a UITableView. I'm getting this error when I try to open the view:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** setObjectForKey: object cannot be nil (key: $in)
Here's how I'm doing it:
#implementation MessageViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self refresh];
_favorited = [[NSMutableArray alloc] initWithCapacity:1000];
}
- (void)refresh {
NSArray *favorite_ids = [PFUser currentUser][#"favorites"];
PFQuery *query = [PFQuery queryWithClassName:#"Messages"];
[query whereKey:#"objectId" containedIn:favorite_ids];
[query findObjectsInBackgroundWithBlock:^(NSArray *projects, NSError *error) {
if (error) {
NSLog(#"Error: %#", error.localizedDescription);
return;
}
PFQuery *query = [PFQuery queryWithClassName:#"MoreMessages"];
[query whereKey:#"objectId" containedIn:favorite_ids];
[query findObjectsInBackgroundWithBlock:^(NSArray *companies, NSError *error) {
if (error) {
NSLog(#"Error: %#", error.localizedDescription);
return;
}
[_favorited setArray:[projects arrayByAddingObjectsFromArray:companies]];
NSLog(#"%#", _favorited);
[_refreshControl endRefreshing];
[self.tableView reloadData];
[SVProgressHUD dismiss];
}];
}];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (_favorited.count < 0) {
return 1;
}
return self.favorited.count;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (_favorited.count < 0) {
return 127;
}
return 65;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (_favorited.count < 0) {
NoFavoritesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"NoFavoritesTableViewCell"];
return cell;
}
MessagesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MessagesTableViewCell"];
_favoritedObject = [_favorited objectAtIndex:indexPath.row];
[(PFFile*)_favoritedObject[#"profilePic"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
cell.profilePic.image = [UIImage imageWithData:data];
}];
cell.name.text = _favoritedObject[#"name"];
return cell;
}
#end
All the null parse values must be set to [NSNull null] and will be received as [NSNull null].
It is inconvenience on many levels as you should check for every single level if it is [NSNull null] or not. I use 2 convenience methods:
- (id)nsNullOrObject:(id)object
{
if(object == nil)
{
return [NSNull null];
}
else
{
return object;
}
}
- (id)objectOrNil:(id)object
{
if(object == [NSNull null])
{
return nil;
}
else
{
return object;
}
}
The first is used when assigning a value to a Parse object and the second when receiving the value from the Parse object.

UitableViewCell not properly displaying accessories

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.

table view numberOfRowsInSection returning 0

I have a table view which I wish to populate with data stored in parse.com.
However nothing loads in my table view. I've done a few breakpoints and NSLog's and found that my numberOfRowsInSection method is always returning 0.
Here is my code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int num = [_totalDistance count];
NSLog(#"num = %d", num);
return [_totalDistance count];
}
activityTypeArray is populated from data collected from parse.com in my viewDidLoad method, is it possible this is the problem?
Here is where _totalDistance is populated:
- (void)viewDidLoad
{
_activityTypeArray = [NSMutableArray array];
_totalDistance = [NSMutableArray array];
PFQuery *activityQuery = [PFQuery queryWithClassName:#"Activities"];
[activityQuery whereKey:#"triathlete" equalTo:[PFUser currentUser]];
[activityQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
;
NSString *activityType = [object objectForKey:#"activityType"];
[_activityTypeArray addObject:activityType];
NSString *totalDistance = [object objectForKey:#"totalDistance"];
[_totalDistance addObject:totalDistance];
NSLog(#" total distance %#", _totalDistance);
int num = [_activityTypeArray count];
NSLog(#"num = %d", num);
}
}
}];
}
Thanks
Modify your code to reload tableView data. Otherwise your tableView won't know you have data to show.
[activityQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
;
NSString *activityType = [object objectForKey:#"activityType"];
[_activityTypeArray addObject:activityType];
NSString *totalDistance = [object objectForKey:#"totalDistance"];
[_totalDistance addObject:totalDistance];
NSLog(#" total distance %#", _totalDistance);
int num = [_activityTypeArray count];
NSLog(#"num = %d", num);
// line added
[self.tableView reloadData];
}
}
}];
You are logging another value (_activityTypeArray count) than you are returning as result (_totalDistance count)
So the NSLog statement cannot help

findObjectsInBackgroundWithBlock shows no results

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;
}

Resources