UISearchResultsController crashing on device (index beyond bounds for empty array) - iphone

I'm using UISearchResultsController to filter data from a fetchedResultsController. Here is the relevant code:
In RootVieweController.h:
#interface RootViewController : UITableViewController <NSFetchedResultsControllerDelegate, AdditionViewControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {
NSArray *filteredListContent;
NSString *savedSearchTerm;
NSInteger savedScopeButtonIndex;
BOOL searchIsActive;
}
#property (nonatomic, retain) NSArray *filteredListContent;
#property (nonatomic, copy) NSString *savedSearchTerm;
#property (nonatomic) NSInteger savedScopeButtonIndex;
#property (nonatomic) BOOL searchIsActive;
In RootViewController.m:
-(void)viewDidLoad {
[snip]
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 300, 40)];
searchBar.delegate = self;
searchBar.scopeButtonTitles = [NSArray arrayWithObjects:#"Scope 1", #"Scope 2", nil];
[searchBar sizeToFit];
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.tableView.tableHeaderView = searchBar;
[searchBar release];
[self.tableView setContentOffset:CGPointMake(0, 40)];
UISearchDisplayController *searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
[self performSelector:#selector(setSearchDisplayController:) withObject:searchDisplayController];
[searchDisplayController setDelegate:self];
[searchDisplayController setSearchResultsDataSource:self];
[searchDisplayController setSearchResultsDelegate:self];
[searchDisplayController release];
self.filteredListContent = [NSMutableArray arrayWithCapacity:[[[self fetchedResultsController] fetchedObjects] count]];
if (self.savedSearchTerm) {
[self.searchDisplayController setActive:self.searchIsActive];
[self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
[self.searchDisplayController.searchBar setText:savedSearchTerm];
self.savedSearchTerm = nil;
}
}
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
if (theTableView == self.searchDisplayController.searchResultsTableView) {
NSLog(#"Search Cells: %i", [self.filteredListContent count]);
return [self.filteredListContent count];
}
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
NSLog(#"Normal cells: %i", [sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
-(void)configureCell:(CustomTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
//Called from cellForRowAtIndexPath
Object *object = nil;
if (self.searchIsActive) {
object = [[self filteredListContent] objectAtIndex:[indexPath row]];
} else {
object = [fetchedResultsController objectAtIndexPath:indexPath];
}
[snip]
}
#pragma mark -
#pragma mark Search functions
-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope {
if ([scope isEqualToString:#"Scope 1"]) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"scope1 BEGINSWITH[cd] %#", searchText];
self.filteredListContent = [[[self fetchedResultsController] fetchedObjects] filteredArrayUsingPredicate:predicate];
} else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"scope2 BEGINSWITH[cd] %#", searchText];
self.filteredListContent = [[[self fetchedResultsController]fetchedObjects] filteredArrayUsingPredicate:predicate];
}
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)theTableView {
NSLog(#"Showing search results");
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:
[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
NSLog(#"Reloading for string");
return YES;
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
NSLog(#"Reloading for scope");
return YES;
}
-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
self.searchDisplayController.searchResultsTableView.rowHeight = 55;
self.searchIsActive = YES;
}
-(void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
self.searchIsActive = NO;
}
This works great in the simulator. But on a device, it crashes on object = [[self filteredListContent] objectAtIndex:[indexPath row]]; from configureCell when trying to show the searchResultsTableView. I get the error [NSMutableArray objectAtIndex:]: index 7 beyond bounds for empty array. Always index 7. What am I missing here?
--UPDATE--
Fixed: flipped the searchIsActive BOOL to switch on searchDisplayControllerDidBeginSearch instead of searchDisplayControllerWillBeginSearch and searchDisplayControllerWillEndSearch instead of searchDisplayControllerDidEndSearch. That keeps the table from trying to configure cells that don't exist. No clue why the Simulator didn't catch this

If the array it is referencing is empty, it doesn't matter what index it is if you are trying to populate the tableview from an empty array.
Is your NSMutableArray filteredListContent populated (i.e. are you returning search results from the fetched results controller)? Have you checked the contents of the array in filterContentForSearchText:?
P.S. I like your style... BeerListTableViewCell :)

I ran into this exact same crash while working on an update to an existing app. The code is almost exactly the same as the original poster's and the only thing that changed in my app was the database. There were no code changes.
With the old database, the table view datasource methods don't get called when the search display controller starts a search.
With the new database, the table view datasource methods do get called when the search display controller starts a search.
Same Core Data entities, same database structure, same code, same nibs. Same problem in simulator or on the device.

Related

I have big delay using search in my UITableView

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

Uncaught exception error! Simple document app

Removed the colon which wasn't supposed to be there since my selector doesn't have one. It fixed it, then this problem occurred:
2012-06-02 22:33:10.083 TinyPix[10433:fb03] load OK
2012-06-02 22:33:10.084 TinyPix[10433:fb03] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<BIDTinyPixDocument 0x6e665a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key timeStamp.'
*** First throw call stack:
(0x16b0022 0x1841cd6 0x16afee1 0x9c8efe 0x937831 0x936c99 0x5f47 0x5e77 0x93af30 0x93aedb 0x59ed 0x4546e3 0x40778d9 0x4078509 0x15e7803 0x15e6d84 0x15e6c9b 0x15997d8 0x159988a 0x1b626 0x2a5d 0x29c5)
terminate called throwing an exception
Here's my detail file: I'm getting a SIGABRT error on the self.detailDescriptionLabel.text line. Don't know what's going on.
#import "BIDDetailViewController.h"
#interface BIDDetailViewController ()
- (void)configureView;
#end
#implementation BIDDetailViewController
#synthesize detailItem = _detailItem;
#synthesize detailDescriptionLabel = _detailDescriptionLabel;
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [[self.detailItem valueForKey:#"timeStamp"] description];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
self.detailDescriptionLabel = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
Here's my file:
#import "BIDMasterViewController.h"
#import "BIDTinyPixDocument.h"
#import "BIDDetailViewController.h"
#interface BIDMasterViewController () <UIAlertViewDelegate>
#property (strong, nonatomic) NSArray *documentFilenames;
#property (strong, nonatomic) BIDTinyPixDocument *chosenDocument;
-(NSURL *)urlForFilename:(NSString *)filename;
-(void)reloadFiles;
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation BIDMasterViewController
#synthesize colorControl;
#synthesize documentFilenames;
#synthesize chosenDocument;
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
- (void)awakeFromNib
{
[super awakeFromNib];
}
-(void)viewWillAppear:(BOOL)animated;
{
[super viewWillAppear:animated];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSInteger selectedColorIndex = [prefs integerForKey:#"selectedColorIndex"];
self.colorControl.selectedSegmentIndex = selectedColorIndex;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
[self reloadFiles];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)insertNewObject
{
// get the name
UIAlertView *alert =
[[UIAlertView alloc] initWithTitle:#"Filename"
message:#"Enter a name for your new TinyPix document."
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Create", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
}
#pragma mark - Table View
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// The table view should not be re-orderable.
return NO;
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
/*
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
}
*/
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[object valueForKey:#"timeStamp"] description];
}
-(NSURL *)urlForFilename:(NSString *)filename {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentDirectory stringByAppendingPathComponent:filename];
NSURL *url = [NSURL fileURLWithPath:filePath];
return url;
}
-(void)reloadFiles {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSFileManager *fm = [NSFileManager defaultManager];
NSError *dirError;
NSArray *files = [fm contentsOfDirectoryAtPath:path error:&dirError];
if (!files) {
NSLog(#"Encountered error while trying to list files in directory %#: %#", path, dirError);
}
NSLog(#"found files: %#", files);
files = [files sortedArrayUsingComparator:^NSComparisonResult(id filename1, id filename2) {
NSDictionary *attr1 = [fm attributesOfItemAtPath:[path stringByAppendingPathComponent:filename1]
error:nil];
NSDictionary *attr2 = [fm attributesOfItemAtPath:[path stringByAppendingPathComponent:filename2]
error:nil];
return [[attr2 objectForKey:NSFileCreationDate] compare:[attr1 objectForKey:NSFileCreationDate]];
}];
self.documentFilenames = files;
[self.tableView reloadData];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.documentFilenames count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FileCell"];
NSString *path = [self.documentFilenames objectAtIndex:indexPath.row];
cell.textLabel.text = path.lastPathComponent.stringByDeletingPathExtension;
return cell;
}
-(IBAction)chooseColor:(id)sender {
NSInteger selectedColorIndex = [(UISegmentedControl *)sender selectedSegmentIndex];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setInteger:selectedColorIndex forKey:#"selectedColorIndex"];
}
-(void)alertView:(UIAlertView *)alertView
didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
NSString *filename = [NSString stringWithFormat:#"%#.tinypix",
[alertView textFieldAtIndex:0].text];
NSURL *saveUrl = [self urlForFilename:filename];
self.chosenDocument = [[BIDTinyPixDocument alloc] initWithFileURL:saveUrl];
[chosenDocument saveToURL:saveUrl
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
NSLog(#"save OK");
[self reloadFiles];
[self performSegueWithIdentifier:#"masterToDetail"
sender:self];
} else {
NSLog(#"failed to save!");
}
}];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (sender == self) {
// if sender == self, a new document has just been created,
// and chosenDocument is already set.
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDetailItem:)]) {
[destination setValue:self.chosenDocument forKey:#"detailItem"];
}
}else {
// find the chose document from the tableview
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *filename = [documentFilenames objectAtIndex:indexPath.row];
NSURL *docUrl = [self urlForFilename:filename];
self.chosenDocument = [[BIDTinyPixDocument alloc] initWithFileURL:docUrl];
[self.chosenDocument openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(#"load OK");
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDetailItem:)]) {
[destination setValue:self.chosenDocument forKey:#"detailItem"];
}
}else {
NSLog(#"failed to load!");
}
}];
}
}
#end
You specify the selector as insertNewObject: and the colon on the end says that it takes one parameter. The insertNewObject method you implemented takes no parameters and, to Objective-C, that makes it a different thing and it can't find the one parameter method.
You should change your implementation to - (IBAction)insertNewObject:(id)sender {...} to match #selector(insertNewObject:).

UITableView does not show new entries when data object conforms to NSCoding

When I make an object class conform to NSCoding, the tableView does not show new entries. Suspected it had to do with the object initialization but cannot solve it. Might be very basic but cannot find the error. Here is the code I simplified to post:
Custom Object:
// DataObject.h
#import <Foundation/Foundation.h>
#interface DataObject : NSObject {
NSString *name;
}
#property (nonatomic, copy) NSString *name;
#end
// DataObject.m
#import "DataObject.h"
#implementation DataObject
#synthesize name;
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) return nil;
name = [[decoder decodeObjectForKey:#"name"] retain];
return self;
}
These is the TableView root controller - Have omitted a few methods:
// RootViewController.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController {
NSMutableArray *list;
}
#property (nonatomic, retain) NSMutableArray *list;
- (void)add:(id)sender;
- (NSString *)dataFilePath;
#end
// RootViewController.m
#import "RootViewController.h"
#import "DataObject.h"
#implementation RootViewController
#synthesize list;
- (void)add:(id)sender
{
DataObject *newEntry = [[DataObject alloc] init];
newEntry.name = #"Willy";
[self.list addObject:newEntry];
[self.tableView reloadData];
// Scroll table view to last row
if ([list count] > 1) {
NSUInteger index = list.count - 1;
NSIndexPath *lastRow = [NSIndexPath indexPathForRow:index inSection: 0];
[self.tableView scrollToRowAtIndexPath: lastRow atScrollPosition: UITableViewScrollPositionTop animated: YES];
}
[newEntry release];
}
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"datafile"];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.title = #"Names";
self.list = [[NSMutableArray alloc] init];
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
initWithTitle:#"Add"
style:UIBarButtonItemStylePlain
target:self
action:#selector(add:)];
self.navigationItem.leftBarButtonItem = addButton;
[addButton release];
[self.tableView reloadData];
}
- (void)applicationWillResignActive:(NSNotification *)notification;
{
[NSKeyedArchiver archiveRootObject:self.list toFile:[self dataFilePath]];
}
- (void)viewWillAppear:(BOOL)animated {
[self.tableView reloadData];
[super viewWillAppear:animated];
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [list 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];
}
// Configure the cell.
NSUInteger row = [indexPath row];
DataObject *oneName = [self.list objectAtIndex:row];
cell.textLabel.text = oneName.name;
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source.
NSUInteger row = [indexPath row];
[self.list removeObjectAtIndex:row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#end
Oh, man I am so embarrassed...It had nothing to do with NSCoding directly but when I made the data class NSCoding compliant I added in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
The first line is fine, but the second assigned a new pointer address to self.list and the tempArray was later autoreleased...
As this line was not executed in the "not NSCoding compliant" version it appeared to work then and not when encodeWithCoder and initWithCoder were implemented.
Because of this self.list was nil instead of being initialized and ready when in the add: method I added a new instance. It was created fine, but never added to the array and therefore never showed in the tableView.
This was easily fixed using instead in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
[self.list addObjectsFromArray:tempArray];
Worst part is that I was pulling my hair because of this and was convinced it was because of NSCoding and the initialization of objects...

Persisting Checklists on a UITableView using NSUserDefults

I have a very simple table view which shows a list of days. Once the user selects which days are relevant to them this data is saved in NSUserdefaults. I then need the check marks to remain once the user has exited then re-entered the table view.
I am very close to getting my desired functionality - I can save the array of check marked items and get it to persist using NSUserDefaults but I don't know how to make these selections persist (keep a check mark next to the selected item) once a user has exited then re-entered the table view.
I know that I need to edit the cellForRowAtIndexPath method but I am not sure exactly what to do. Any help would be greatly appreciated.
I have attached my code below:
#import "DayView.h"
#implementation DayView
#synthesize sourceArray;
#synthesize selectedArray;
- (id)init
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
// Custom initialization
[[self navigationItem] setTitle:#"Days"];
[[self tableView] setBackgroundColor:[UIColor clearColor]];
}
return self;
}
- (void)viewWillDisappear:(BOOL)animated
{
// create a standardUserDefaults variable
NSUserDefaults * standardUserDefaults = [NSUserDefaults standardUserDefaults];
// Convert array to string
NSString *time = [[selectedArray valueForKey:#"description"] componentsJoinedByString:#","];
// saving an NSString
[standardUserDefaults setObject:time forKey:#"string"];
NSLog(#"Disapear: %#", time);
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// create a standardUserDefaults variable
NSUserDefaults * standardUserDefaults = [NSUserDefaults standardUserDefaults];
// getting an NSString object
NSString *myString = [standardUserDefaults stringForKey:#"string"];
NSLog(#"Appear: %#", myString);
NSMutableArray * tempArray = [[NSMutableArray alloc] init];
[self setSelectedArray:tempArray];
[tempArray release];
NSArray * tempSource = [[NSArray alloc] initWithObjects:#"Monday", #"Tuesday", #"Wednesday", #"Thursday", #"Friday", #"Saturday",nil];
[self setSourceArray:tempSource];
[tempSource release];
[self.tableView reloadData];
}
#pragma mark -
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.sourceArray count];;
}
// 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] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSString *time = [sourceArray objectAtIndex:indexPath.row];
cell.textLabel.text = time;
if ([self.selectedArray containsObject:[self.sourceArray objectAtIndex:indexPath.row]])
{
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
else
{
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
NSLog(#"Selected Days: %#", selectedArray);
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return #"Which times you are available?";
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([self.selectedArray containsObject:[self.sourceArray objectAtIndex:indexPath.row]]){
[self.selectedArray removeObjectAtIndex:[self.selectedArray indexOfObject: [self.sourceArray objectAtIndex:indexPath.row]]];
}
else
{
[self.selectedArray addObject:[self.sourceArray objectAtIndex:indexPath.row]];
}
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
Create your tempSource as follows:
NSMutableArray * tempSource = [[NSMutableArray alloc] init];
NSArray *daysOfWeek = [NSArray arrayWithObjects:#"Monday", #"tuestay", #"wednesday", #"thursday", #"friday", #"saturday", #"sunday",nil];
for (int i = 0; i < 7; i++)
{
NSString *dayOfWeek = [daysOfWeek objectAtIndex:i];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:dayOfWeek, #"day", [NSNumber numberWithBool:NO], #"isSelected",nil];
[tempSource addObject:dict];
}
[self setSourceArray:tempSource];
[tempSource release];
Then use this Array in cellForRow and didSelect as follows:
cellForRow
NSDictionary *dayOfWeekDictionary = [sourceArray objectAtIntex:indexPath.row];
cell.textLabel.text = [dayOfWeekDictionary objectForKey:#"day"];
if ([[dayOfWeekDictionary objectForKey:#"isSelected"] boolValue])
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
else
[cell setAccessoryType:UITableViewCellAccessoryNone];
didSelect
NSDictionary *dayOfWeekDictionary = [sourceArray objectAtIntex:indexPath.row];
if ([[dayOfWeekDictionary objectForKey:#"isSelected"] boolValue])
[dayOfWeekDictionary setObject:[NSNumber numberWithBool:NO] forKey:#"isSelected"];
else
[dayOfWeekDictionary setObject:[NSNumber numberWithBool:YES] forKey:#"isSelected"];
To save this Array use this statement:
[[NSUserDefaults standardUserDefaults] setObject:sourceArray forKey:#"array"];

iPhone TableView Search XML

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