I am implementing a search bar controller to search a table view. The below method code which performs the search is crashing with the error "-[__NSArrayM rangeOfString:options:]: unrecognized selector sent to instance 0x65558e0'
The locationInfo array is an array containing 26 arrays, each of which contains a number of objects made up of strings.
Can anyone suggest why the code is crashing ?
Thank you.
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSString *currentString in [self locationInfo])
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentString];
}
}
}
}
As you stated in question that "locationInfo" is array containing 26 arrays,
so,
currentString in [self locationInfo] will return an array only so try to write something like below :
for (NSArray *currentArray in [self locationInfo])
{
for (NSString *currentString in currentArray)
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentString];
}
}
}
or something like this
Based on the error you're getting, it seems [self locationInfo] returns an array (NSArray) and not a string (NSString) as you're expecting.
Related
In my app, i have a search bar in my contact list page tableview. now my code searches the list based on any letter even if the search text is at the middle of the firstname or lastname. But i want it to search only from the beginning. For example., the word "sh" should pull only "Shiva", "Sheela", etc., but not "sathish", "suresh" etc., can anyone help me on this?
and my code is
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchText
{
//---if there is something to search for---
if ([searchText length] > 0)
{
isSearchOn = YES;
canSelectRow = YES;
self.ContactTableview.scrollEnabled = YES;
searchTextValue = searchText;
[searchResult removeAllObjects];
for (NSString *str in ContactArray)
{
NSRange range = [str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if(range.location != NSNotFound)
{
if(range.length > 0)//that is we are checking only the start of the names.
{
[searchResult addObject:str];
}
}
}
}
else
{
//---nothing to search---
isSearchOn = NO;
canSelectRow = NO;
self.ContactTableview.scrollEnabled = YES;
//SearchBar.showsCancelButton = NO;
[TitleBarLabel setText:#"All Contacts"];
}
[ContactTableview reloadData];
}
try with predicates,in below code replace your values.
NSPredicate *p = [NSPredicate predicateWithFormat:#"SELF BEGINSWITH[cd] %#",#"A"];
NSArray *a = [NSArray arrayWithObjects:#"AhfjA ", #"test1", #"Test", #"AntA", nil];
NSArray *b = [a filteredArrayUsingPredicate:p];
NSLog(#"--%#",b);
O/P:-
(
AntA,
AntA
)
Im developing an app, need to load some data in background , then show the data using UITableView.
Here are some codes,
loading data in background:
- (void)loadRelatedItems
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (NSString *mediaType in allMediaTypes)
{
[self performSelector:#selector(loadRelatedItems:) withObject:mediaType];
}
NSString *notificationName = [CommonFunction allRelatedItemsLoadedNotificationName];
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:nil];
[pool release];
}
- (void)loadRelatedItems:(NSString *)mediaType
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (NSString *keyword in _keywords)
{
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"%#&mediaType=%#&keyword=%#", API, mediaType, keyword]];
NSMutableArray *items = [CommonFunctions arrayFromURL:URL];
if ([items count] == 0) continue;
NSString *notificationName = [CommonFunction partialRelatedItemsLoadedNotificationName];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:items, #"items", mediaType, #"mediaType", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:dic];
}
[pool release];
}
showing the data in UITableView:
- (void)didFinishLoadPartialRelatedItems:(id)sender
{
NSDictionary *dic = [sender userInfo];
NSString *mediaTypeString = [dic objectForKey:#"mediaType"];
NSMutableArray *items = [dic objectForKey:#"items"];
dispatch_async(dispatch_get_main_queue(), ^{
if ([_relatedItems count] == 0)
{
[_relatedItems setObject:items forKey:mediaTypeString];
[_tableView reloadData];
}
else
{
NSMutableArray *mediaTypeItems = [_relatedItems objectForKey:mediaTypeString];
if (mediaTypeItems)
{
// section exist
NSInteger section =[[[_relatedItems allKeys] sortedArrayUsingSelector:#selector(mediaTypeCompare:)] indexOfObject:mediaTypeString];
NSMutableArray *indexPaths = [NSMutableArray array];
for (NSMutableDictionary *item in items)
{
[mediaTypeItems addObject:item];
NSInteger newRow = [mediaTypeItems indexOfObject:item];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:newRow inSection:section];
[indexPaths addObject:newIndexPath];
}
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[_tableView endUpdates];
}
else
{
// new section
[_relatedItems setObject:items forKey:mediaTypeString];
NSInteger section =[[[_relatedItems allKeys] sortedArrayUsingSelector:#selector(mediaTypeCompare:)] indexOfObject:mediaTypeString];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:section];
[_tableView beginUpdates];
[_tableView insertSections:indexSet withRowAnimation:UITableViewRowAnimationNone];
[_tableView endUpdates];
}
}
});
}
#pragma mark -
#pragma mark Table Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if ([_relatedItems count] == 0) {
return 1;
} else {
return [_relatedItems count];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *allTitles = [[_relatedItems allKeys] sortedArrayUsingSelector:#selector(mediaTypeCompare:)];
NSString *title = [allTitles objectAtIndex:section];
NSDictionary *allMediaTypeDisplayNames = [CommonFunction allMediaTypeDisplayNames];
return [allMediaTypeDisplayNames objectForKey:title];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([_relatedItems count] == 0) {
return 0;
}
NSArray *allTitles = [[_relatedItems allKeys] sortedArrayUsingSelector:#selector(mediaTypeCompare:)];
NSString *title = [allTitles objectAtIndex:section];
NSInteger rowsCount = [[_relatedItems objectForKey:title] count];
return rowsCount;
}
I'm very confused that it works fine some times, but some times it crashed with message:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1030
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (0) must be equal to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
What's the problem? please help.
Please make sure after updating, your number of sections should be equal to number of sections before the update.
As per your code :
the number of sections are defined as like this:
if ([_relatedItems count] == 0) {
return 1;
} else {
return [_relatedItems count];
}
and in this case you are creating new section right ?
else { // new section
[_relatedItems setObject:items forKey:mediaTypeString];
NSInteger section =[[[_relatedItems allKeys] sortedArrayUsingSelector:#selector(mediaTypeCompare:)] indexOfObject:mediaTypeString];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:section];
[_tableView beginUpdates];
[_tableView insertSections:indexSet withRowAnimation:UITableViewRowAnimationNone];
[_tableView endUpdates];
}
If you are creating new section then your [_relatedItems count] is increasing. So, please make sure after the inserting also your count should be same.
Right ?
Try This :
if ([_relatedItems count] == 0) {
return 1;
} else {
if([_relatedItems count]>previousCount)
return [_relatedItems count];
return previousCount;
}
when ever you are making any updates to the [_relatedItems]; then change update your previousCount also.. this will be solved
I think your problem is with the number of rows method in the datasource just do one thing define int noOfRows in your .h file .assign your table view array count with the noOfRows. noOfRows=[yourtableArray count];
then return noOfRows from table views numberOfRowsInSection method.
Add 1 to noOfRows if you insert row in the table.
Make noOfRows-- when you delete row from the table
You will not get this exception .Update your array as per your requirement.
Could anyone give me a point in the right direction on how I should do this:
This sample from the Weather Channel shows what I would like to do. I just want to have a Table View in which someone could search for a city. I'm not sure where to get those resources and how to do it.
You can find the World City List from MaxMind.
Is it the resource you are looking for?
Create table view and search bar. You have to implement their delegates UISearchBarDelegate,UITableViewDataSource,UITableViewDelegate.
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar1
{
searchBar.showsSearchResultsButton = YES;
searchBar.showsCancelButton = YES;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
// flush the previous search content
//Implement some code
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar1
{
searchBar.showsCancelButton = NO;
searchBar.showsSearchResultsButton = NO;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if([searchText isEqualToString:#""]||searchText==nil){
[yourTable reloadData];
return;
}
NSInteger counter = 0;
for(NSString *name in yourArray)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSRange r = [name rangeOfString:searchText options:NSCaseInsensitiveSearch];
// NSRange r = [name rangeOfString:searchText];
// NSRange r = [name ];
if(r.location != NSNotFound)
{
//Implement the code.
}
counter++;
[pool release];
}
[yourTable reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar1
{
// if a valid search was entered but the user wanted to cancel, bring back the main list content
// Implement some code
#try{
[yourTable reloadData];
}
#catch(NSException *e){
}
[searchBar resignFirstResponder];
searchBar.text = #"";
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar1
{
[searchBar1 resignFirstResponder];
}
I have give to you incomplete methods and you can implement what you want to do.
I think it will be helpful to you.
I use a plist file to get a list of site which are displayed in a tableview
The plist looks like this:
<array>
<dict>
<key>site</key>
<string>http://yahoo.com</string>
<key>title</key>
<string>Yahoo</string>
</dict>
<dict>
<key>site</key>
<string>http://google.com</string>
<key>title</key>
<string>Google</string>
</dict>
//...etc
</array>
</plist>
I display this without problem:
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"TestData" ofType:#"plist"]];
[self setContentsList:array];
*The problem is when I try to search in the content and I want to get the valueforkey #"site" from the search result to use it in didSelectRowAtIndexPath: *
NSMutableArray *contentsList;
NSMutableArray *searchResults;
NSString *savedSearchTerm;
---------------------
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSString *currentString in [[self contentsList] valueForKey:#"title"])
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentString];
// NSDictionary *dic= [[NSDictionary alloc]allKeysForObject:searchResults];
}
}
}
}
The didSelectRowAtIndexPath is used to open the site in a webView
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *arraySite = [[[self searchResults] objectAtIndex:indexPath.row] valueForKey:#"site"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:arraySite]]];
[self performSelector:#selector(showSearch:) withObject:nil afterDelay:0];
}
ERROR I GET :
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSCFString 0x6042730> valueForUndefinedKey:]: this class is not key value coding-compliant for the key site.'
The Basics
When you read the array from that plist, the array looks like the following:
(
{
"site" = "http://yahoo.com",
"title" = "Yahoo"
},
{
"site" = "http://google.com",
"title" = "Google"
},
…
)
It is an array whose elements are dictionaries, each dictionary containing two keys and their corresponding values.
When you use the KVC method -valueForKey: on the array passing the key title, it returns another array whose elements are the values corresponding to that key:
(
"Yahoo",
"Google",
…
)
The resulting array doesn’t hold a reference to the original array.
The Problem
In -handleSearchForTerm:, you get an array containing only the titles from the original array. For each title, you selectively add it to the searchResults array:
for (NSString *currentString in [[self contentsList] valueForKey:#"title"])
{
…
[[self searchResults] addObject:currentString];
}
This means that searchResults is an array containing a list of titles which are not automatically related to the corresponding dictionaries in the contentList array.
It seems like you want to keep the original dictionary because you’ve tried to create a dictionary:
// NSDictionary *dic= [[NSDictionary alloc]allKeysForObject:searchResults];
and, in another method, you’re trying to obtain the value corresponding to the site key:
NSString *arraySite = [[[self searchResults] objectAtIndex:indexPath.row]
valueForKey:#"site"];
As mentioned, your searchResults contains a list of strings representing titles. When you obtain an element from this array, it’s only a string — hence -valueForKey:#"site" doesn’t make sense, and Cocoa warns you that a string is not key-value compliant for the key site.
One Solution
From what I can tell, you should be storing, in your searchResults array, the original dictionary read from the plist file. In -handleSearchForTerm:, do the following:
for (NSDictionary *currentSite in [self contentsList])
{
NSString *title = [currentSite objectForKey:#"title"];
if ([title rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentSite];
}
}
Now every element in searchResults is a dictionary containing both site and title.
In -tableView:didSelectRowAtIndexPath:, use the dictionary to obtain the corresponding site:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *selectedSite = [[self searchResults] objectAtIndex:indexPath.row];
NSString *siteStringURL = [selectedSite objectForKey:#"site"];
// or, if you prefer everything in a single line:
// NSString *siteStringURL = [[[self searchResults] objectAtIndex:indexPath.row] objectForKey:#"site"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:siteStringURL]]];
[self performSelector:#selector(showSearch:) withObject:nil afterDelay:0];
}
I am attempting to implement searching in a UITableView. When searching, it appears that the correct number of results are returned, but I am receiving entries from the original stories array in the results, rather than searchResults. I can see that the searchResults array should be the data source, but haven't been able to figure out after tons of searching quite how to pull it off with an array of NSDictionaries. Any help is appreciated.
- (void)handleSearchForTerm:(NSString *)searchTerm {
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSDictionary *currentItem in [self stories])
{
if ([[currentItem objectForKey:#"title"] rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentItem];
}
}
}
}
[tableView isEqual:self.searchDisplayController.searchResultsTableView] is also another alternative to making and managing your own BOOL isFiltering; variable
use NSPredicate for filtering
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self.title MATCHES %#",searchTerm];
Suppose that your original array is "originalArray" so to get the filtered array use this make two more global variables
NSArray* filteredArray;
BOOL isFiltering;
Now in search bar delegate method do following
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self.title MATCHES %#",searchTerm];
filteredArray = [[originalArray filteredArrayUsingPredicate:predicate] retain];
}
Now you need to change l'll bit your table view delegate and data source, .... for all the places where you are using
NSDictionary *currentString = [originalArray objectAtIndex:indexPath.row];
use following
NSDictionary *currentString;
if(isFiltering)
currentString = [originalArray objectAtIndex:indexPath.row];
else
currentString = [filteredArray objectAtIndex:indexPath.row];