I m fairly new to objective-C and implementing the concept of autocomplete feature for a UItextField.I can do it appropriately .but when I select a particular cell then that cell's text should be displayed in UITextField and correspondingly tableView must be hidden.But Im unable to hide a UITableView after selecting a cell..Where I m going wrong?
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
NSURL *urlString= [NSString stringWithFormat:#"http://179.87.89.90/services/Service.svc/GetCities/?p=%#&k=%#",substring,txtId.text];
NSURL *jsonUrl =[NSURL URLWithString:urlString];
NSString *jsonStr = [[NSString alloc] initWithContentsOfURL:jsonUrl];
parser = [[NSXMLParser alloc] initWithContentsOfURL:jsonUrl];
currentHTMLElement=#"3";
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
[parser release];
NSLog(#"%#",arr2);
if([arr2 count]!=0)
{
self.autocompleteUrls = [[NSMutableArray alloc] init];
autocompleteTableView = [[UITableView alloc] initWithFrame:CGRectMake(88, 447, 200, 120) style:UITableViewStyleGrouped];
autocompleteTableView.delegate = self;
autocompleteTableView.dataSource = self;
autocompleteTableView.scrollEnabled = YES;
// autocompleteTableView.hidden = YES;
[self.view addSubview:autocompleteTableView];
[autocompleteUrls removeAllObjects];
for(int i=0;i<[arr2 count];i++)
{
NSString *curString = [[arr2 objectAtIndex:i] valueForKey:#"Name"];
NSRange substringRange = [curString rangeOfString:substring];
if (substringRange.location == 0)
[autocompleteUrls addObject:curString];
}
[autocompleteTableView reloadData];
}
else
{
autocompleteTableView.delegate=nil;
autocompleteTableView.dataSource=nil;
autocompleteTableView.hidden = YES;
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if( textField == txtcity)
{
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 autocompleteUrls.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] autorelease];
}
cell.textLabel.text = [autocompleteUrls objectAtIndex:indexPath.row];
cell.textLabel.font=[UIFont boldSystemFontOfSize:12];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
txtcity.text = selectedCell.textLabel.text;
[autocompleteUrls removeAllObjects];
[self.autocompleteTableView setHidden:YES];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
txtcity.text = selectedCell.textLabel.text;
[autocompleteUrls removeAllObjects];
autocompleteTableView.hidden=YES;
}
How can I hide autocompleteTableView after selecting a row ?
Any help would be appreciated..
The issue is with the if condition, when the if is evaluated true then again the autocompleteTableView is again allocated and added to self.view. It'll be placed over the previous tableView and youare losing the reference of previous tableView. If you call autocompleteTableView.hidden = YES. Last added tableView will be hidden. But previously added tableViews will be there.
Just change the if block like:
if([arr2 count]!=0)
{
self.autocompleteUrls = [[NSMutableArray alloc] init];
if(autocompleteTableView)
[autocompleteTableView removeFromSuperView];
autocompleteTableView = [[UITableView alloc] initWithFrame:CGRectMake(88, 447, 200, 120) style:UITableViewStyleGrouped];
autocompleteTableView.delegate = self;
autocompleteTableView.dataSource = self;
autocompleteTableView.scrollEnabled = YES;
// autocompleteTableView.hidden = YES;
[self.view addSubview:autocompleteTableView];
for(int i=0;i<[arr2 count];i++)
{
NSString *curString = [[arr2 objectAtIndex:i] valueForKey:#"Name"];
NSRange substringRange = [curString rangeOfString:substring];
if (substringRange.location == 0)
[autocompleteUrls addObject:curString];
}
[autocompleteTableView reloadData];
}
#Arizah - please try making a fresh app to test the UItableview & UItextField delegate methods. It might be that somewhere --
autocompleteTableView.delegate=nil;
autocompleteTableView.dataSource=nil;
gets called due to which further no delegate method like :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
WILL NOT BE CALLED. TRY AVOIDING THIS CODE.
Also the statement: self.autocompleteUrls = [[NSMutableArray alloc] init];
is technically incorrect since a retain property needs no allocation. Instead you can use:
NSMutableArray *theArray= [[NSMutableArray alloc] init];
self.autocompleteUrls = theArray;
[theArray release];
did you try:
[self.tableView setHidden:YES];
Related
I have a uitableview that's displaying multiple selections with a custom checkmark. When selected the rows value is save using NSUserDefaults. The problem is that despite the values being saved the checkmarks disappear from the table cell rows. I can't figure out why.
thanks for any help, I'm really stuck on this.
Here's the .h code:
#interface CategoriesViewController : UITableViewController {
NSString *selectedCategoryTableString;
NSString *jsonStringCategory;
int prev;
}
// arForTable array will hold the JSON results from the api
#property (nonatomic, retain) NSArray *arForTable;
#property (nonatomic, retain) NSMutableArray *arForIPs;
#property (nonatomic, retain) NSMutableArray *categorySelected;
#property (nonatomic, retain) NSString *jsonStringCategory;
#property(nonatomic, retain) UIView *accessoryView;
#end
and the .m code:
#implementation CategoriesViewController
#synthesize jsonStringCategory;
#synthesize arForTable = _arForTable;
#synthesize arForIPs = _arForIPs;
- (void)viewDidLoad
{
[super viewDidLoad];
self.arForIPs=[NSMutableArray array];
self.categorySelected = [[NSMutableArray alloc] init];
[self reloadMain];
self.tableView.allowsMultipleSelection = YES;
}
-(void) reloadMain {
jsonString = #"http:///******";
// Download the JSON
NSString *jsonString = [NSString
stringWithContentsOfURL:[NSURL URLWithString:jsonString]
encoding:NSStringEncodingConversionAllowLossy|NSUTF8StringEncoding
error:nil];
NSMutableArray *itemsTMP = [[NSMutableArray alloc] init];
// Create parser
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary *results = [parser objectWithString:jsonString error:nil];
itemsTMP = [results objectForKey:#"results"];
self.arForTable = [itemsTMP copy];
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.arForTable count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[cell.textLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
[cell.detailTextLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
cell.accessoryView.hidden = NO;
}
UIImageView *cellAccessoryImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon-tick.png"]] ;
UIImageView *cellAccessoryNoneImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#""]] ;
if([self.arForIPs containsObject:indexPath]){
cell.accessoryView = cellAccessoryImageView;
} else {
cell.accessoryView = cellAccessoryNoneImageView;
}
// Get item from tableData
NSDictionary *item = (NSDictionary *)[_arForTable objectAtIndex:indexPath.row];
// encoding fix
NSString *utf8StringTitle = [item objectForKey:#"name"];
NSString *correctStringTitle = [NSString stringWithCString:[utf8StringTitle cStringUsingEncoding:NSISOLatin1StringEncoding] encoding:NSUTF8StringEncoding];
cell.textLabel.text = [correctStringTitle capitalizedString];
NSNumber *num = [item objectForKey:#"id"];
cell.detailTextLabel.text = [num stringValue];
cell.detailTextLabel.hidden = YES;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if([self.arForIPs containsObject:indexPath]){
[self.arForIPs removeObject:indexPath];
[self.categorySelected removeObject:[[self.arForTable objectAtIndex:indexPath.row] objectForKey:#"id"]];
} else {
[self.arForIPs addObject:indexPath];
[self.categorySelected addObject:[[self.arForTable objectAtIndex:indexPath.row] objectForKey:#"id"]];
NSLog(#"%# categorySelected",self.categorySelected);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(#"%# defaults categorySelected",[defaults arrayForKey:#"selectedCategoryTableString"]);
NSString *string = [self.categorySelected componentsJoinedByString:#","];
[defaults setObject:string forKey:#"selectedCategoryTableString"];
NSLog(#"%# STRING",string);
}
[tableView reloadData];
}
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:NO];
[self.navigationController setNavigationBarHidden:YES animated:NO];
self.navigationController.toolbarHidden = YES;
}
First of all your code has lots of memory leaks, please do use the static analyzer and/or instruments to fix them, few for them are pretty obvious like you initialized the SBJSON parser and did not release it, itemsTMP is another.
I have rewritten your code to be much more efficient and memory friendly:
#interface CategoriesViewController : UITableViewController
{
NSArray *_items;
NSMutableArray *_selectedItems;
UIImageView *cellAccessoryImageView;
}
#end
#implementation CategoriesViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_selectedItems = [NSMutableArray new];
cellAccessoryImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon-tick.png"]] ;
[self reloadMain];
self.tableView.allowsMultipleSelection = YES;
}
- (void)reloadMain
{
NSString *jsonString = #"http:///******";
// Download the JSON
jsonString = [NSString
stringWithContentsOfURL:[NSURL URLWithString:jsonString]
encoding:NSStringEncodingConversionAllowLossy|NSUTF8StringEncoding
error:nil];
// Create parser
SBJSON *parser = [SBJSON new];
NSDictionary *results = [parser objectWithString:jsonString error:nil];
if (_items) [_items release];
_items = [[results objectForKey:#"results"] copy];
[parser release];
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
[cell.textLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
[cell.detailTextLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
cell.accessoryView.hidden = NO;
}
NSDictionary *item = [_items objectAtIndex:indexPath.row];
if ([_selectedItems containsObject:item])
{
// preloaded image will help you have smoother scrolling
cell.accessoryView = cellAccessoryImageView;
}
else
{
cell.accessoryView = nil;
cell.accessoryType = UITableViewCellAccessoryNone;
}
// Get item from tableData
cell.textLabel.text = [[NSString stringWithCString:[[item objectForKey:#"name"] cStringUsingEncoding:NSISOLatin1StringEncoding] encoding:NSUTF8StringEncoding] capitalizedString];
cell.detailTextLabel.text = [[item objectForKey:#"id"] stringValue];
cell.detailTextLabel.hidden = YES;
item = nil;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *item = [_items objectAtIndex:indexPath.row];
if ([_selectedItems containsObject:item])
{
[_selectedItems removeObject:item];
}
else
{
[_selectedItems addObject:item];
}
item = nil;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (void)dealloc
{
[_selectedItems release];
[cellAccessoryImageView release];
[super dealloc];
}
#end
Since in your table there is only one section. Try this approach and this will help you certainly.
In - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath write following code;
if([self.arForIPs containsObject:[NSNumber numberWithInt:indexPath.row]]){
cell.accessoryView = cellAccessoryImageView;
} else {
cell.accessoryView = cellAccessoryNoneImageView;
}
And in - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath write code as below,
if([self.arForIPs containsObject:[NSNumber numberWithInt:indexPath.row]]){
[self.arForIPs removeObject:[NSNumber numberWithInt:indexPath.row]];
} else {
[self.arForIPs addObject:[NSNumber numberWithInt:indexPath.row]]
}
I have a UITextView which is connected to UITableView for Autocompletion.
The problem is the table display is not properly formatted, i have following problems with this:
the details are not in order(ex: if i press a it is not displaying words starts with a first, it display as it likes).
i have three kind of words in my txt file(like Apple, A pple, A.pple);
in my table it display only A pple and A.pple but not Apple if i start search with letter 'Ap' even it displays A pple till i write 'A P' then it stops displaying the words.
Can any one let me know what to do that?
Please find my code for your reference:
Sorry for posting all the code, i am doing because i cannot find were its going wrong!!!
- (void) finishedSearching {
[usernameField resignFirstResponder];
autoCompleteTableView.hidden = YES;
}
- (void)viewDidLoad
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"employee.txt" ofType:nil];
NSData* data = [NSData dataWithContentsOfFile:filePath];
//Convert the bytes from the file into a string
NSString* string = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding];
//Split the string around newline characters to create an array
NSString* delimiter = #"\n";
NSArray *item = [string componentsSeparatedByString:delimiter];
elementArray = [[NSMutableArray alloc] initWithArray:item];
usernameField = [[UITextField alloc] initWithFrame:CGRectMake(204, 405, 264, 31)];
usernameField.borderStyle = 3; // rounded, recessed rectangle
usernameField.autocorrectionType = UITextAutocorrectionTypeNo;
usernameField.autocapitalizationType = UITextAutocapitalizationTypeNone;
usernameField.textAlignment = UITextAlignmentLeft;
usernameField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
usernameField.returnKeyType = UIReturnKeyDone;
usernameField.font = [UIFont fontWithName:#"Trebuchet MS" size:20];
usernameField.textColor = [UIColor blackColor];
usernameField.placeholder=#"Login id";
[usernameField setDelegate:self];
[self.view addSubview:usernameField];
autoCompleteArray = [[NSMutableArray alloc] init];
autoCompleteTableView = [[UITableView alloc] initWithFrame:CGRectMake(204, 450, 264, tableHeight) style:UITableViewStylePlain];
autoCompleteTableView.delegate = self;
autoCompleteTableView.dataSource = self;
autoCompleteTableView.scrollEnabled = YES;
autoCompleteTableView.hidden = YES;
autoCompleteTableView.rowHeight = tableHeight;
[self.view addSubview:autoCompleteTableView];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
// Put anything that starts with this substring into the autoCompleteArray
// The items in this array is what will show up in the table view
[autoCompleteArray removeAllObjects];
for(NSString *curString in elementArray) {
NSRange substringRangeLowerCase = [curString rangeOfString:[substring lowercaseString]];
NSRange substringRangeUpperCase = [curString rangeOfString:[substring uppercaseString]];
if (substringRangeLowerCase.length != 0 || substringRangeUpperCase.length != 0) {
[autoCompleteArray addObject:curString];
}
}
autoCompleteTableView.hidden = NO;
[autoCompleteTableView reloadData];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
[super touchesBegan:touches withEvent:event];
[self finishedSearching];
}
#pragma mark UITextFieldDelegate methods
// Close keyboard when Enter or Done is pressed
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
BOOL isDone = YES;
if (isDone) {
[self finishedSearching];
return YES;
} else {
return NO;
}
}
// String in Search textfield
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *substring = [NSString stringWithString:textField.text];
substring = [substring stringByReplacingCharactersInRange:range withString:string];
[self searchAutocompleteEntriesWithSubstring:substring];
return YES;
}
#pragma mark UITableViewDelegate methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
//Resize auto complete table based on how many elements will be displayed in the table
if (autoCompleteArray.count >=3) {
autoCompleteTableView.frame = CGRectMake(204, 450, 264, tableHeight*3);
return autoCompleteArray.count;
}
else if (autoCompleteArray.count == 2) {
autoCompleteTableView.frame = CGRectMake(204, 450, 264, tableHeight*2);
return autoCompleteArray.count;
}
else {
autoCompleteTableView.frame = CGRectMake(204, 450, 264, tableHeight);
return autoCompleteArray.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 = [autoCompleteArray objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
usernameField.text = selectedCell.textLabel.text;
usernameField.text=[usernameField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSLog(#"%#",usernameField.text);
[self finishedSearching];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
Thanks in Advance!!!!
follow this code.. U have done all correctly but searching the element is the problem. U have written the code. if your checking length alone. if its not null it will show data. but u have to compare the first letter with element array. so that it works fine.
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
[autocompleteArray removeAllObjects];
for(NSString *curString in elementArray) {
NSRange substringRange = [curString rangeOfString:substring];
if (substringRange.location == 0) {
[autocompleteArray addObject:curString];
}
[autocompleteTableView reloadData];
}
}
There is my problem, i have an application which have decent amount of data. My .plist file contain array of elements, it looks like this - Orange, proteins - 25, carbs - 40, fat - 50, etc.. Totally, each item contain 7 rows of sub-rows with data.
My tableview show all array displayed with sections, in one big massive tableview. In top of screen i have a search bar. When i tap search bar, and enter any letter, it show new array like this - M: Big Mac, Meat, Meals, etc.
So, before i reach 700+ elements in my array everything was just fine, but, when i added last 500 elements (my .plist file edited from 200 to 700 elements), i realize that when i tap search and enter any letter, i have big delay. First delay about 0,6 sec (when i tap search field), second delay after i press button in my keyboard (about 0,6 sec again). I think, that is because i add many items to my .plist.
Obviously, i don't want to reduce number of my objects in array. I guess i have "bad" code, and i ask you for any helpful advice. Please help me, i guess i need to improve it! There is my code, that contain my UITableView code and Search methods:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"bg.png"]];
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:#"food.plist"];
listOfItems = [[NSMutableArray alloc]initWithContentsOfFile:path];
searchListOfItems = [[NSMutableArray alloc]init];
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 45)];
searchBar.barStyle = UIBarStyleBlackTranslucent;
searchBar.showsCancelButton = NO;
searchBar.autocorrectionType=UITextAutocorrectionTypeNo;
searchBar.autocapitalizationType=UITextAutocapitalizationTypeNone;
searchBar.delegate= self;
[[self tableView] setTableHeaderView:searchBar];
searching = NO;
letUserSelectRow = YES;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addProduct:)];
self.navigationItem.leftBarButtonItem = addButton;
self.navigationItem.backBarButtonItem.title = #"Back";
[self.tableView reloadData];
self.navigationItem.backBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:#"Назад"
style:UIBarButtonItemStyleBordered
target:nil
action:nil];
self.title = #"Продукты";
UILabel* tlabel=[[UILabel alloc] initWithFrame:CGRectMake(0,0, 125, 21)];
tlabel.text=self.navigationItem.title;
tlabel.font = [UIFont fontWithName:#"Chalkboard SE" size:17];
tlabel.textAlignment = UITextAlignmentCenter;
tlabel.textColor=[UIColor whiteColor];
tlabel.backgroundColor =[UIColor clearColor];
tlabel.adjustsFontSizeToFitWidth=YES;
self.navigationItem.titleView=tlabel;
}
- (void)hideModalViewController:(NSNotification *)notif
{
[self dismissModalViewControllerAnimated:YES];
[self viewDidLoad];
}
-(void)productAdded {
[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentSize.height - self.tableView.frame.size.height)];
[self.tableView reloadData];
}
- (void)addProduct:(UIBarButtonItem *)button
{
BIDAddProductViewController *addProductVC = [[BIDAddProductViewController alloc]init];
addProductVC.delegate = self;
[self.navigationController pushViewController:addProductVC animated:YES];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(hideModalViewController:) name:#"HideModalViewController" object:addProductVC];
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.childController = nil;
self.tableView=nil;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
if (editingStyle == UITableViewCellEditingStyleDelete)
{
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:#"food.plist"];
NSMutableArray *listOfItemsToDelete = [[NSMutableArray alloc]initWithContentsOfFile:path];
[[[listOfItemsToDelete objectAtIndex:indexPath.section]objectForKey:#"Products"] removeObjectAtIndex:indexPath.row];
[listOfItemsToDelete writeToFile:path atomically:YES];
[self viewDidLoad];
NSArray *descriptionsArray = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"ProductName"];
NSLog(#"%#", descriptionsArray);
NSLog(#"%i", [descriptionsArray count]);
if ([descriptionsArray count]<2){
[listOfItems removeObjectAtIndex:indexPath.section];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:NO];
[listOfItems writeToFile:path atomically:YES];
}
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SectionsTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:SectionsTableIdentifier];
}
if(searching)
cell.textLabel.text = [[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"ProductName"];
else {
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"ProductName"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (searching)
return 1;
else
return [listOfItems count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (searching)
return [searchListOfItems count];
else {
NSDictionary *dictionary = [listOfItems objectAtIndex:section];
NSArray *array = [dictionary objectForKey:#"Products"];
return [array count];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if(searching)
return #"";
return [[listOfItems objectAtIndex:section]valueForKey:#"SectionName"];
}
- (void) doneSearching_Clicked:(id)sender {
searchBar.text = #"";
[searchBar resignFirstResponder];
letUserSelectRow = YES;
searching = NO;
self.navigationItem.rightBarButtonItem = nil;
self.tableView.scrollEnabled = YES;
[self.tableView reloadData];
}
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
[self searchTableView];
}
- (void) searchTableView {
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
for (NSDictionary *dictionary in listOfItems)
{
NSArray *array = [dictionary objectForKey:#"Products"];
[searchArray addObjectsFromArray:array];
}
for (NSString *sTemp in [searchArray valueForKeyPath:#"ProductName"] )
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
[searchListOfItems addObject:[searchArray objectAtIndex:[[searchArray valueForKeyPath:#"ProductName"]indexOfObject:sTemp]]];
}
searchArray = nil;
}
- (NSIndexPath *)tableView :(UITableView *)theTableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(letUserSelectRow)
return indexPath;
else
return nil;
}
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {
searching = YES;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:#selector(doneSearching_Clicked:)];
}
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {
[searchListOfItems removeAllObjects];
if([searchText length] > 0) {
searching = YES;
letUserSelectRow = YES;
self.tableView.scrollEnabled = YES;
[self searchTableView];
}
else {
searching = NO;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
}
[self.tableView reloadData];
}
- (void)viewWillAppear:(BOOL)animated
{
letUserSelectRow = YES;
[super viewWillAppear:animated];
}
#pragma mark -
#pragma mark Table Delegate Methods
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (childController == nil) {
childController = [[BIDDisclosureDetailController alloc]
initWithNibName:#"BIDDisclosureDetail" bundle:nil];
}
if(searching)
{
childController.description = [[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"ProductName"];
// childController.title = [[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"ProductName"];
childController.calories = [[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"Calories"];
childController.protein = [[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"Proteins"];
childController.carbohydrates = [[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"Carbohydrates"];
childController.fat = [[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"Fat"];
childController.myBool=[[searchListOfItems objectAtIndex:indexPath.row]valueForKey:#"TextField"];
}
else
{
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *descriptionsArray = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"ProductName"];
childController.description = [descriptionsArray objectAtIndex:indexPath.row];
NSArray *proteinArray = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"Proteins"];
childController.protein = [proteinArray objectAtIndex:indexPath.row];
NSArray *carbohydratesArray = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"Carbohydrates"];
childController.carbohydrates = [carbohydratesArray objectAtIndex:indexPath.row];
NSArray *fatArray = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"Fat"];
childController.fat = [fatArray objectAtIndex:indexPath.row];
NSArray *caloriesArray = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"Calories"];
childController.calories = [caloriesArray objectAtIndex:indexPath.row];
NSArray *textFieldArray = [[dictionary objectForKey:#"Products"]valueForKeyPath:#"TextField"];
childController.myBool = [textFieldArray objectAtIndex:indexPath.row];
}
[self.navigationController pushViewController:childController
animated:YES];
}
#end
In your code every time you change the text in the search bar the searchTableView function is run. You will want to make sure this function is as quick as possible.
I see you are iterating over the list of NSDictionarys from the plist file in the function and building a search array. If the plist is large this is probably quite expensive of an operation to be doing on every text change.
for (NSDictionary *dictionary in listOfItems)
{
NSArray *array = [dictionary objectForKey:#"Products"];
[searchArray addObjectsFromArray:array];
}
I think if you moved this iteration to viewDidLoad and made searchArray a global variable you will notice some speed increases.
Hope this helps.
I think you should use SqliteDatabase to store so much data,and use SQL to search your data.Every time you touch search bar,all of your data alloc in a new NSMutableArray with the method
-(void)searchTableView
this is why when you touch the search bar , it will delay 0.6sec when your data is too much
I have a question about adding XML to the searchbar in a tableview. I can get all the external XML file to load in the tableview, but when I hit the searchbar up top, and hit a letter, it crashes.
I think it's something really simple that I'm doing wrong. In my RootViewController, there's a function called searchTableView. I feel like that's where it's not picking up the search items. I think it's somewhere around the objectForKey:#"title". When I debug, I get this error message also: "NSCFArray objectForKey unrecognized selector". Here's my searchTableView function:
- (void) searchTableView {
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
for (NSDictionary *dictionary in listOfItems)
{
NSArray *array = [dictionary objectForKey:#"title"];
[searchArray addObjectsFromArray:array];
}
for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
[copyListOfItems addObject:sTemp];
}
[searchArray release];
searchArray = nil;
}
Ok figured it out. For some reason this was really hard to find documentation how to do this.
Here's my RootViewController.m below.
My pList is configured as:
Root (Array)
Item0 (Dictionary)
Name (String)
Item1 (Dictionary)
Name (String)..
Here's my code, hopefully this helps anyone else looking for help on this:
#implementation RootViewController
#synthesize listOfItems, copyListOfItems;
- (void)viewDidLoad {
[super viewDidLoad];
//Initialize the array.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"plistArray" ofType:#"plist"];
NSMutableArray* tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
self.listOfItems = tmpArray;
[tmpArray release];
//Initialize the copy array.
copyListOfItems = [[NSMutableArray alloc] init];
//Set the title
self.navigationItem.title = #"Search";
//Add the search bar
self.tableView.tableHeaderView = searchBar;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
searching = NO;
letUserSelectRow = YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (searching)
return 1;
else
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (searching)
return [copyListOfItems count];
else {
//Number of rows it should expect should be based on the section
return [listOfItems count];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the selected country
NSString *selectedCountry = nil;
if(searching)
selectedCountry = [copyListOfItems objectAtIndex:indexPath.row];
else {
// Navigation logic may go here. Create and push another view controller.
}
NSDictionary *dictionary = [self.listOfItems objectAtIndex:indexPath.row];
FoodDetail *dvController = [[FoodDetail alloc] initWithNibName:#"FoodDetail" bundle:nil andDictionary: dictionary];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
if(searching)
cell.textLabel.text = [copyListOfItems objectAtIndex:indexPath.row];
else {
cell.textLabel.text = [[self.listOfItems objectAtIndex:indexPath.row]
objectForKey:#"Name"];
}
return cell;
}
- (NSIndexPath *)tableView :(UITableView *)theTableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(letUserSelectRow)
return indexPath;
else
return nil;
}
#pragma mark -
#pragma mark Search Bar
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {
//This method is called again when the user clicks back from the detail view.
//So the overlay is displayed on the results, which is something we do not want to happen.
if(searching)
return;
//Add the overlay view.
if(ovController == nil)
ovController = [[OverlayViewController alloc] initWithNibName:#"OverlayView" bundle:[NSBundle mainBundle]];
CGFloat yaxis = self.navigationController.navigationBar.frame.size.height;
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;
//Parameters x = origion on x-axis, y = origon on y-axis.
CGRect frame = CGRectMake(0, yaxis, width, height);
ovController.view.frame = frame;
ovController.view.backgroundColor = [UIColor grayColor];
ovController.view.alpha = 0.5;
ovController.rvController = self;
[self.tableView insertSubview:ovController.view aboveSubview:self.parentViewController.view];
searching = YES;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
//Add the done button.
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:#selector(doneSearching_Clicked:)] autorelease];
}
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {
//Remove all objects first.
[copyListOfItems removeAllObjects];
if([searchText length] > 0) {
[ovController.view removeFromSuperview];
searching = YES;
letUserSelectRow = YES;
self.tableView.scrollEnabled = YES;
[self searchTableView];
}
else {
[self.tableView insertSubview:ovController.view aboveSubview:self.parentViewController.view];
searching = NO;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
}
[self.tableView reloadData];
}
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
[self searchTableView];
}
- (void) searchTableView {
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
for (NSDictionary *dictionary in listOfItems)
{
NSString *text1 = [dictionary objectForKey:#"Name"];
[searchArray addObject:text1];
}
NSLog(#"%s: searchArray=%#", __func__, searchArray);
for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
[copyListOfItems addObject:sTemp];
}
[searchArray release];
searchArray = nil;
}
- (void) doneSearching_Clicked:(id)sender {
searchBar.text = #"";
[searchBar resignFirstResponder];
letUserSelectRow = YES;
searching = NO;
self.navigationItem.rightBarButtonItem = nil;
self.tableView.scrollEnabled = YES;
[ovController.view removeFromSuperview];
[ovController release];
ovController = nil;
[self.tableView reloadData];
}
- (void)dealloc {
[ovController release];
[copyListOfItems release];
[searchBar release];
[listOfItems release];
[super dealloc];
}
#end
I really can't get this to work, basically when my JSON feeds loads I want the indicator to show, then hide when it's stopped.
It loads top level menu items 1st "Publishing, Broadcasting, Marketing Services", then when Broadcasting is selected it loads a feed using the JSON framework hosted on Google. Round this load I call startIndicator and stopIndicator using the NSThread. Have I missed something?
#implementation GeneralNewsTableViewController
#synthesize dataList;
#synthesize generalNewsDetailViewController;
#synthesize atLevel;
-(void) startIndicator
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init ];
[(UIActivityIndicatorView *)[self navigationItem].rightBarButtonItem.customView startAnimating];
[pool release];
}
-(void) stopIndicator
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init ];
[(UIActivityIndicatorView *)[self navigationItem].rightBarButtonItem.customView stopAnimating];
[pool release];
}
- (void)viewDidLoad {
NSMutableArray *checker = self.dataList;
if(checker == nil)
{
self.title = NSLocalizedString(#"General1",#"General News");
NSMutableArray *array = [[NSArray alloc] initWithObjects:#"Publishing", #"Broadcasting",#"Marketing Services",nil];
self.dataList = [array retain];
self.atLevel = #"level1";
[array release];
}
UIActivityIndicatorView * activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
//set the initial property
[activityIndicator stopAnimating];
[activityIndicator hidesWhenStopped];
//Create an instance of Bar button item with custome view which is of activity indicator
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
//Set the bar button the navigation bar
[self navigationItem].rightBarButtonItem = barButton;
//Memory clean up
[activityIndicator release];
[barButton release];
[super viewDidLoad];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *level = self.atLevel;
if([level isEqualToString:#"level2"])
{
return 70.0f;
}
else
{
return 40.0f;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *FirstLevelCell = #"FirstLevelCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:FirstLevelCell];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:FirstLevelCell] autorelease];
}
NSInteger row = [indexPath row];
NSString *level = self.atLevel;
if([level isEqualToString:#"level2"])
{
NSMutableArray *stream = [self.dataList objectAtIndex:row];
NSString *newsTitle = [stream valueForKey:#"title"];
if( ![newsTitle isKindOfClass:[NSString class]] )
{
cell.textLabel.text = #"";
}
else
{
cell.textLabel.text = [stream valueForKey:#"title"];
}
cell.textLabel.numberOfLines = 2;
cell.textLabel.font =[UIFont systemFontOfSize:10];
cell.detailTextLabel.numberOfLines = 1;
cell.detailTextLabel.font= [UIFont systemFontOfSize:8];
cell.detailTextLabel.text = [stream valueForKey:#"created"];
NSData *imageURL = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://www.website.co.uk/images/stories/img.jpg"]];
UIImage *newsImage = [[UIImage alloc] initWithData:imageURL];
cell.imageView.image = newsImage;
[imageURL release];
[newsImage release];
}
else
{
cell.textLabel.text = [dataList objectAtIndex:row];
}
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];
NSMutableString *levelType = (NSMutableString *) [dataList objectAtIndex:row];
if(![levelType isKindOfClass:[NSString class]])
{
if(self.generalNewsDetailViewController == nil)
{
GeneralNewsDetailViewController *generalDetail = [[GeneralNewsDetailViewController alloc] initWithNibName:#"GeneralNewsDetailView" bundle:nil];
self.generalNewsDetailViewController = generalDetail;
[generalDetail release];
}
NSDictionary *stream = [self.dataList objectAtIndex:row];
NSString *newsTitle = [stream valueForKey:#"title"];
if( ![newsTitle isKindOfClass:[NSString class]] )
{
generalNewsDetailViewController.newsTitle = #"";
}
else
{
generalNewsDetailViewController.newsTitle =[stream valueForKey:#"title"];
}
generalNewsDetailViewController.newsId = [stream valueForKey:#"id"];
generalNewsDetailViewController.fullText = [stream valueForKey:#"fulltext"];
generalNewsDetailViewController.newsImage = [stream valueForKey:#"images"];
generalNewsDetailViewController.created = [stream valueForKey:#"created"];
HowDo_v1AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.generalNewsNavController pushViewController:self.generalNewsDetailViewController animated:YES];
}
else
{
GeneralNewsTableViewController
*generalSubDetail = [[GeneralNewsTableViewController alloc] initWithNibName:#"GeneralNewsTableView" bundle:nil];
NSMutableArray *array;
NSString *titleSelected = (NSString *) [dataList objectAtIndex:row];
if([titleSelected isEqualToString:#"Publishing"])
{
generalSubDetail.title = #"Publishing news detail";
array = [[NSArray alloc] initWithObjects:#"pub News1", #"pub News2",#"pub News3",nil];
generalSubDetail.atLevel = #"level1";
}
else if ([titleSelected isEqualToString:#"Broadcasting"])
{
generalSubDetail.title = #"Broadcasting news detail";
/// START
[self performSelectorOnMainThread:#selector(startIndicator) withObject:nil waitUntilDone:YES];
if(jSONDataAccessWrapper == nil)
{
jSONDataAccessWrapper = [JSON_DataAccess_Wrapper alloc];
}
array = [jSONDataAccessWrapper downloadJSONFeed];
[self performSelectorOnMainThread:#selector(stopIndicator) withObject:nil waitUntilDone:YES];
generalSubDetail.atLevel = #"level2";
}
else if ([titleSelected isEqualToString:#"Marketing Services"])
{
generalSubDetail.title = #"Marketing Services news detail";
array = [[NSArray alloc] initWithObjects:#"Marketing News1", #"Marketing News2",#"Marketing News3",nil];
generalSubDetail.atLevel = #"level1";
}
generalSubDetail.dataList = array;
[self.navigationController pushViewController:generalSubDetail animated:YES];
[titleSelected release];
}
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
//- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSIndexPath *) section
{
return [self.dataList count];
}
Cheers for any feedback
Frames
It is usually recommended by Apple that operations on user interface should be done on the main thread.
What you can do is to defer the callback into the main thread:
- (void)startIndicator
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(startAnimating:) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)startAnimating:(id)sender
{
[(UIActivityIndicatorView *)[self navigationItem].rightBarButtonItem.customView startAnimating];
}
Check out the Thread Guide from Apple. It contains useful information.