I have a TableViewController and array from JSON url. I wanted to search #Name from server.Tableview showing data were well.But searching does not working.How to do it ?Where is the mistake?
Tableviewcontroller.h :
#interface CitiesViewController ()
#property(nonatomic,strong)NSMutableArray *jsonArray
#property(nonatomic,strong)NSMutableArray *citiesArray;
#property (nonatomic, copy)NSMutableArray *filteredNames;
#property (nonatomic, strong)UISearchController *searchController;
#property (strong, readwrite, nonatomic) NSMutableArray *arrayPlace;
#property (weak, nonatomic) IBOutlet UISearchBar *searcBar;
-(void)retrieveData;
Tableviewcontroller.m :
#end
#implementation CitiesViewController
#synthesize
citiesArray,jsonArray,filteredNames,arrayPlace,searcBar,searchController;
#pragma mark -
#pragma mark Class Methods
-(void)retrieveData
{
NSURL *url = [NSURL URLWithString:getDataURL];
NSData *data = [NSData dataWithContentsOfURL:url];
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
citiesArray =[[NSMutableArray alloc]init];
for (int i= 0; i<jsonArray.count; i++)
{
NSString *cID = [[jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString *cName = [[jsonArray objectAtIndex:i] objectForKey:#"Name"];
NSString *cCompany = [[jsonArray objectAtIndex:i] objectForKey:#"company"];
NSString *cPosition = [[jsonArray objectAtIndex:i] objectForKey:#"position"];
NSString *cEmail = [[jsonArray objectAtIndex:i] objectForKey:#"email"];
NSString *cNumber = [[jsonArray objectAtIndex:i] objectForKey:#"number"];
NSString *cPhoto = [[jsonArray objectAtIndex:i] objectForKey:#"photo"];
NSURL *urlOne = [NSURL URLWithString:cPhoto];
// NSLog(#"%#",urlOne);
NSData *photoData = [NSData dataWithContentsOfURL:urlOne];
UIImage *imageOne = [[UIImage alloc] initWithData:photoData];
[citiesArray addObject:[[City alloc]initWithCityName:cName andCityState:cCompany andCityCountry:cEmail andCityPopulation:cPosition andCityID:cID andCityNumber:cNumber andCityPhoto: (UIImage *) imageOne]];
}
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self retrieveData];
self.arrayPlace = [NSMutableArray new];
UITableView *tableView = (id)[self.view viewWithTag:1];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
filteredNames = [[NSMutableArray alloc]init];
searchController = [[UISearchController alloc]init];
self.searchController.searchResultsUpdater = self;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView.tag == 1) {
return citiesArray.count;
} else {
return [filteredNames count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
// Configure the cell...
if (tableView.tag == 1){
City *cityob;
cityob=[citiesArray objectAtIndex:indexPath.row];
cell.textLabel.text=cityob.employeeName;
}else{
cell.textLabel.text = filteredNames[indexPath.row];
}
return cell;
}
#pragma mark Search Display Delegate Methods
-(void)searchDisplayController:(UISearchController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
}
-(BOOL)searchDisplayController:(UISearchController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[filteredNames removeAllObjects];
if (searchString.length > 0) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains [search] %#", self.searcBar.text];
for (NSString *name in citiesArray) {
NSArray *matches = [[self.jsonArray[name] objectForKey:#"Name"]filteredArrayUsingPredicate:predicate];
[filteredNames addObjectsFromArray:matches];
}
NSLog(#"filteredNames==%#",filteredNames);
}
return YES;
}
Related
I'm trying to integrate search bar with my remote table view but it didn't work properly. There were no errors. I think I did some mistakes but I don't know which part so I need your help.
This is my full code:
//
// MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
static NSString *const kConsumerKey = #"a1SNULSPtp4eLQTsTXKKSgXkYB5H4CMFXmleFvqE";
#interface MasterViewController ()<UISearchDisplayDelegate>
#property (nonatomic, assign) NSInteger currentPage;
#property (nonatomic, assign) NSInteger totalPages;
#property (nonatomic, assign) NSInteger totalItems;
#property (nonatomic, assign) NSInteger maxPages;
#property (nonatomic, strong) NSMutableArray *photos;
#property (nonatomic, strong) NSMutableArray *searchResults;
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#end
#implementation MasterViewController
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.photos = [NSMutableArray array];
self.searchResults = [NSMutableArray arrayWithCapacity:[self.photos count]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadPhotos:self.currentPage];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.currentPage == self.maxPages
|| self.currentPage == self.totalPages
|| self.currentPage == self.totalPages
|| self.totalItems == self.photos.count) {
return self.photos.count;
}else if(tableView == self.searchDisplayController.searchResultsTableView){
return [self.searchResults count];
}
return self.photos.count + 1;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.currentPage != self.maxPages && indexPath.row == [self.photos count] - 1 ) {
[self loadPhotos:++self.currentPage];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.row == [self.photos count]) {
cell = [tableView dequeueReusableCellWithIdentifier:#"LoadingCell" forIndexPath:indexPath];
UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)[cell.contentView viewWithTag:100];
[activityIndicator startAnimating];
}else{
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if(tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
}
NSDictionary *photoItem = self.photos[indexPath.row];
cell.textLabel.text = [photoItem objectForKey:#"name"];
if (![[photoItem objectForKey:#"description"] isEqual:[NSNull null]]) {
cell.detailTextLabel.text = [photoItem objectForKey:#"description"];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:[photoItem objectForKey:#"image_url"]]
placeholderImage:[UIImage imageNamed:#"placeholder.jpg"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(#"Error occured : %#", [error description]);
}
}];
}
return cell;
}
#pragma mark UISearchDisplay delegate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
[self.searchResults removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
self.searchResults = [NSMutableArray arrayWithArray:[self.photos filteredArrayUsingPredicate:resultPredicate]];//[self.tableData filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)loadPhotos:(NSInteger)page {
NSString *apiURL = [NSString stringWithFormat:#"https://api.500px.com/v1/photos?feature=editors&page=%ld&consumer_key=%#",(long)page,kConsumerKey];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:apiURL]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (!error) {
NSError *jsonError = nil;
NSMutableDictionary *jsonObject = (NSMutableDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
NSLog(#"%#",jsonObject);
[self.photos addObjectsFromArray:[jsonObject objectForKey:#"photos"]];
self.currentPage = [[jsonObject objectForKey:#"current_page"] integerValue];
self.totalPages = [[jsonObject objectForKey:#"total_pages"] integerValue];
self.totalItems = [[jsonObject objectForKey:#"total_items"] integerValue];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}] resume];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DetailViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.StoreList = [_photos objectAtIndex:indexPath.row];
}
#end
NSDictionary *photoItem = self.photos[indexPath.row];
and later
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchText];
self.searchResults = [NSMutableArray arrayWithArray:[self.photos filteredArrayUsingPredicate:resultPredicate]];
This looks like u try to sort with predicate not String but Dictionary.
Try :
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#", searchText];
Also i'd recommend u to read this code style and here some tutorial how to implement search
Regarding ur second question
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
if (indexPath.row == [self.photos count]) {
cell = [tableView dequeueReusableCellWithIdentifier:#"LoadingCell" forIndexPath:indexPath];
UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)[cell.contentView viewWithTag:100];
[activityIndicator startAnimating];
} else{
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if(tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchResults[indexPath.row] valueForKey:#"name"] } else {
NSDictionary *photoItem = self.photos[indexPath.row];
cell.textLabel.text = [photoItem objectForKey:#"name"];
if (![[photoItem objectForKey:#"description"] isEqual:[NSNull null]]) {
cell.detailTextLabel.text = [photoItem objectForKey:#"description"];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:[photoItem objectForKey:#"image_url"]]
placeholderImage:[UIImage imageNamed:#"placeholder.jpg"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(#"Error occured : %#", [error description]);
}
}];
}
}
return cell;
}
I want to pull results with 2 columns with NSMutableArray.
-"name" column --> use to show in tableview when user start to type something
-"address" colomn -->will be used to send to another view.
Here is what I do :
#interface ViewController ()
{
ViewController *SampleViewController;
NSMutableArray *totalStrings;
NSMutableArray *filteredStrings;
BOOL isFiltered;
}
#implementation ViewController
- (void)viewDidLoad {
[self createOrOpenDB]; ////Call database
NSMutableArray* result=[[NSMutableArray alloc] init];
NSString *ask = [NSString stringWithFormat:#"SELECT * FROM tb_001"];
sqlite3_stmt *statement;
const char *query_statement = [ask UTF8String];
const char *dbpath = [_DBPath UTF8String];
if (sqlite3_open(dbpath, &_iDB) == SQLITE_OK){
{
if (sqlite3_prepare_v2(_iDB, query_statement, -1, &statement, nil) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *nameField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
NSString *addressField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
//add your namefield here.
[result addObject:xxxxxxxxxxxxxxx];
}
sqlite3_finalize(statement);
sqlite3_close(_iDB);
}
}
totalStrings=[[NSMutableArray alloc] initWithArray:result];
}
Here is where I will use the first column
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length ==0){
isFiltered = NO;
}
else
{
isFiltered = YES;
filteredStrings =[[NSMutableArray alloc]init];
for (NSString *str in totalStrings) {
NSRange StringRange = [str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (StringRange.location !=NSNotFound) {
[filteredStrings addObject:str];
}
}
}
[self.myTableView reloadData];
}
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[self.myTableView resignFirstResponder];
}
//table view datasource and delegate methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (isFiltered){
return [filteredStrings count];
}
return [totalStrings count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier= #"Cell";
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell){
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
if(!isFiltered){
cell.textLabel.text =[totalStrings objectAtIndex:indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else{ //It is filtered
cell.textLabel.text = [filteredStrings objectAtIndex:indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//here I want to use result from another column (address)
LadyGagaVC *dada =[self.storyboard
[dada setText:[resilt_from_anothercolomn objectAtIndex:indexPath.row]];
}
How can I put it in the correct array and use it ?
I know I have to use for loop but right now I have no idea.
You can use NSDictionary, like this:
NSString *nameField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
NSString *addressField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
NSDictionary *data = #{#"name":nameField,
#"address":addressField};
//add your namefield here.
[result addObject:data];
And then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//here I want to use result from another column (address)
LadyGagaVC *vc = ...
NSDictionary *dic = totalStrings[indexPath.row];
NSString *name = dic[#"name"];
NSString *address = dic[#"address"];
//Use this values as you want...
}
Or it would be better to make a class (model) , say, MyModel which will have two property:
#interface MyModel : NSObject
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *address;
#end
And instead of dictionaries use instances of MyModel. E.G.
MyModel *model = [MyModel new];
model.name = nameField;
model.address = addressField;
[result addObject:model];
Not sure where I went wrong. My understanding is that importing UIKit will enable use of the numberOfSectionsInTableView method... Any help is appreciated.
//HEADER
#import <UIKit/UIKit.h>
#interface MainScrollingTableVC : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
int selectedIndex;
NSMutableArray *titleArray;
NSArray *subtitleArray;
NSArray *textArray;
}
#end
//IMPLEMENTATION
#import "MainScrollingTableVC.h"
#import "MainExpandingCellTableViewCell.h"
#interface MainScrollingTableVC ()
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation MainScrollingTableVC
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
//index = -1 means no cell is expanded or should expand
selectedIndex = -1;
titleArray = [[NSMutableArray alloc] init];
NSString *string;
//create consecutive array of 8 numbers. 1...2...etc...
for(int ii = 1; ii <= 8; ii++) {
string = [[NSString alloc] initWithFormat:#"Row %i", ii];
[titleArray addObject:string];
}
subtitleArray = [[NSArray alloc] initWithObjects:#"First row", #"Second row", #"Third row", #"Fourth row", #"Fifth row", #"Sixth row", #"Seventh row", #"Eigth row", nil];
textArray = [[NSArray alloc] initWithObjects:#"Apple", #"Orange", #"Grape", #"Banana", #"Kiwi", #"Blueberry", #"Apricot", #"Lemon", nil];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return titleArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"MainExpandingCell";
MainExpandingCell *cell = (MainExpandingCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MainExpandingCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
if(selectedIndex == indexPath.row) {
//Do expanded cell stuff
} else {
//Do closed cell stuff
}
cell.titleLabel.text = [titleArray objectAtIndex:indexPath.row];
cell.timeLabel.text = [subtitleArray objectAtIndex: indexPath.row]; //CHECK VARS
cell.textLabel.text = [textArray objectAtIndex:indexPath.row];
int etaLabel = (indexPath.row + 1) * 25;
cell.etaLabel.text = [NSString stringWithFormat:#"%i", eta]
cell.clipsToBounds = YES;
return cell;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You are declaring your methods inside the viewDidLoad method. Objective-C doesn't support method nesting. Fix your whole code with its formatting and add the missing } accordingly.
I have interfaces like:
#interface Isle
#property (nonatomic, strong) id index_one; //has to be an id ={
#property (nonatomic, strong) id index_two;
#end
#interface Item
#property (nonatomic, strong) NSString* name; //has to be an NSString
#end
Then I instantiate a search view controller:
#import "TableViewController.h"
#import "Isle.h"
#import "Item.h"
#interface TableViewController () <UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, strong) NSArray* isles;
#property (nonatomic, strong) NSArray* filter;
#end
#implementation TableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self)
{
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
Isle* isle_one = [[Isle alloc] init];
Isle* isle_two = [[Isle alloc] init];
Isle* isle_three = [[Isle alloc] init];
isle_one.index_one = [[Item alloc] init];
isle_one.index_two = [[Item alloc] init];
isle_two.index_one = [[Item alloc] init];
isle_two.index_two = [[Item alloc] init];
isle_three.index_one = [[Item alloc] init];
isle_three.index_two = [[Item alloc] init];
((Item*)isle_one.index_one).name = #"ketchup";
((Item*)isle_one.index_two).name = #"hot sauce";
((Item*)isle_two.index_one).name = #"butter";
((Item*)isle_two.index_two).name = #"cheese";
((Item*)isle_three.index_one).name = #"cereal";
((Item*)isle_three.index_two).name = #"rice";
_isles = [[NSArray alloc] initWithObjects: isle_one, isle_two, isle_three, nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate* res = [NSPredicate predicateWithFormat:#"(index_one.name contains[cd] %#) OR (index_two.name contains[cd] %#)", #"ketchup", #"hot sauce"];
_filter = [self.isles filteredArrayUsingPredicate: res];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [[self filter] count];
}
return [[self isles] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"CellID" forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CellID"];
}
if (tableView == self.searchDisplayController.searchResultsTableView)
{
Isle* isle = (Isle*)[self.filter objectAtIndex: indexPath.row];
NSString* str = [NSString stringWithFormat:#"%#\t\t%#", ((Item*)isle.index_one).name, ((Item*)isle.index_two).name];
cell.textLabel.text = str;
return cell;
}
Isle* isle = (Isle*)[self.isles objectAtIndex: indexPath.row];
NSString* str = [NSString stringWithFormat:#"%# %#", ((Item*)isle.index_one).name, ((Item*)isle.index_two).name];
cell.textLabel.text = str;
return cell;
}
#end
But it says that it is not key-value compliant. Any ideas how I can accomplish this?
EDIT:
My exact error is:
Project1[1688:60b] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSSymbolicExpression 0x10e60a650> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.'
For filtering I did:
[arr filteredArrayUsingPredicate: res];
I'm having issues with implementing the UISearchBarDelegate on my UITableView with sections issue where every time I try to search for something inside the UITableView, either it doesn't display the desired result or it crashes the app with an error saying:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
Here's inside my header file:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate>
{
UITableView *mainTableView;
NSMutableArray *contentsList;
NSMutableArray *searchResults;
NSString *savedSearchTerm;
NSMutableArray *ivSectionKeys;
NSMutableDictionary *ivSectionContents;
}
#property (nonatomic, retain) IBOutlet UITableView *mainTableView;
#property (nonatomic, retain) NSMutableArray *contentsList;
#property (nonatomic, retain) NSMutableArray *searchResults;
#property (nonatomic, copy) NSString *savedSearchTerm;
#property (nonatomic, retain) NSMutableArray *sectionKeys;
#property (nonatomic, retain) NSMutableDictionary *sectionContents;
- (void)handleSearchForTerm:(NSString *)searchTerm;
#end
while this is inside my implementation file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize mainTableView;
#synthesize contentsList;
#synthesize searchResults;
#synthesize savedSearchTerm;
#synthesize sectionKeys = ivSectionKeys;
#synthesize sectionContents = ivSectionContents;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableArray *keys = [[NSMutableArray alloc] init];
NSMutableDictionary *contents = [[NSMutableDictionary alloc] init];
NSString *colorKey = #"Colors";
NSString *clothingKey = #"Clothing";
NSString *miscKey = #"Misc";
[contents setObject:[NSArray arrayWithObjects:#"Red", #"Blue", nil] forKey:colorKey];
[contents setObject:[NSArray arrayWithObjects:#"Pants", #"Shirt", #"Socks", nil] forKey:clothingKey];
[contents setObject:[NSArray arrayWithObjects:#"Wankle Rotary Engine", nil] forKey:miscKey];
[keys addObject:clothingKey];
[keys addObject:miscKey];
[keys addObject:colorKey];
self.sectionKeys = keys;
self.sectionContents = contents;
// Restore search term
if (self.savedSearchTerm)
{
self.searchDisplayController.searchBar.text = self.savedSearchTerm;
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Save the state of the search UI so that it can be restored if the view is re-created.
self.savedSearchTerm = self.searchDisplayController.searchBar.text;
self.searchResults = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.mainTableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)handleSearchForTerm:(NSString *)searchTerm
{
self.savedSearchTerm = searchTerm;
if (self.searchResults == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
self.searchResults = array;
array = nil;
}
[self.searchResults removeAllObjects];
if ([self.savedSearchTerm length] != 0)
{
for (NSString *currentString in self.sectionContents)
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[self.searchResults addObject:currentString];
}
}
}
}
#pragma mark -
#pragma mark UITableViewDataSource Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSInteger sections = [self.sectionKeys count];
return sections;
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section
{
NSString *key = [self.sectionKeys objectAtIndex:section];
return key;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
NSString *key = [self.sectionKeys objectAtIndex:section];
NSArray *contents = [self.sectionContents objectForKey:key];
NSInteger rows;
if (tableView == [[self searchDisplayController] searchResultsTableView])
rows = [self.searchResults count];
else
rows = [contents count];
NSLog(#"rows is: %d", rows);
return rows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *key = [self.sectionKeys objectAtIndex:indexPath.section];
NSArray *contents = [self.sectionContents objectForKey:key];
NSString *contentForThisRow = [contents objectAtIndex:indexPath.row];
if (tableView == [self.searchDisplayController searchResultsTableView])
contentForThisRow = [self.searchResults objectAtIndex:indexPath.row];
else
contentForThisRow = [contents objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = contentForThisRow;
return cell;
}
#pragma mark -
#pragma mark UITableViewDelegate Methods
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self handleSearchForTerm:searchString];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
self.savedSearchTerm = nil;
[self.mainTableView reloadData];
}
#end
I think my issue is inside my for-loop but I'm not really sure.
You need to update all of your table view data source and delegate methods to handle both tables (you only do this in some):
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger sections = [self.sectionKeys count];
return sections;
}
should be:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView == self.tableView) {
NSInteger sections = [self.sectionKeys count];
return sections;
} else { // assume it's the search table
// replace this with the actual result
return numberOfSectionsForSearchTable;
}
}