I am writing an iOS chat application wherein I have to show the users list with a search bar. I am getting the updated user list from the server after every 2 min.
The problem that I am facing is
Case:1
If I have the search bar active and I call reload table when the datasource is updated, now if I cancel the search, I do not get my original table. Only the filtered results are shown.
Case: 2
If I have the search bar active and I just update the data source and do not reload the table, now if I cancel the search, it loops to cellforrowatindexpath as many times as there are elements in the new datasource but it does not show the updated elements. Infact, it jumbles up the elements because it considers the new index paths but uses the old data.
By jumbled up I mean,
Say if the old datasource is a1, a2, a3 and a4 with avatar images a1, a2, a3, a4.
The new datasource is a5, a1, a2, a3, a4 with images a5, a1, a2, a3, a4.
So the jumbled view that I get is
a1 with a5 image,
a2 with a1 image,
a3 with a4 image
a4 with no image.
4 cells instead of 5.
Case: 3 (working correctly)
If I have the search bar active and I do not even update the data source, nothing happens when I cancel the search (as expected). I have to reload the table to get the updated view.
What I want is that it should update the table even if search is active and when I cancel the search I should get the updated table view. I know this can be achieved by reload table when cancel button is clicked but I am trying to find an elegant solution to this.
Also, please tell me the concept (the inner workings) behind why is this working like this.
My code:
- (void)refreshUserList
{
if(dtclass.updatedUserList && [resultsArray count] ==0)
{
userArray = dtclass.getUpdatedUserList;
[self.tableView reloadData];
[dtclass setUpdatedUserList:NO];
}
else if(dtclass.updatedUserList && [resultsArray count] !=0)
{
//Uncomment for Case:1
/*userArray = dtclass.getUpdatedUserList;
[dtclass setUpdatedUserList:NO];
[self.tableView reloadData];*/
//Uncomment for Case:2
/*userArray = dtclass.getUpdatedUserList;
[dtclass setUpdatedUserList:NO];*/
//remove else if code for Case:3
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
if (resultsArray.count != 0)
{
return [resultsArray count];
}
return userArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView setSeparatorInset:UIEdgeInsetsMake(0, 70, 0, 0)];
GlobalListTableViewCell *cell = (GlobalListTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"GlobalListCell"];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"GlobalListTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
//custome class User
User *currentUser;
if(resultsArray.count !=0)
{
currentUser = [resultsArray objectAtIndex:indexPath.row];
}
else
{
currentUser = [userArray objectAtIndex:indexPath.row];
}
cell.nameLabel.text = currentUser.getName;
cell.timeLabel.text = #"5 pm";
dispatch_async(fetchImage, ^{
if([[currentUser getAvatarUrl] length]!=0)
{
[self loadImagesWithURL:[NSString stringWithFormat:#"%#%#",#"http:",[currentUser getAvatarUrl]] IndexPath:indexPath];
}
});
cell.avatarImage.image = [UIImage imageNamed:#"defaultUser.png"];
cell.messageLabel.text = #"No message";
return cell;
}
-(void) loadImagesWithURL:(NSString *)imageURL IndexPath:(NSIndexPath *)indexPath
{
NSURL *url = [NSURL URLWithString:imageURL];
NSData *data = [[NSData alloc ] initWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
GlobalListTableViewCell *cell = (GlobalListTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.avatarImage.image = img;
});
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath
{
return 60;
}
- (void)filterContentForSearchText:(NSString*)searchText scope: (NSString*)scope
{
resultsArray = [[NSArray alloc] init];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.getName contains[c] %#", searchText];
resultsArray = [userArray 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)searchBarCancelButtonClicked:(UISearchBar *) aSearchBar {
aSearchBar.text = nil;
}
Try this code for search ..
- (void)filterContentForSearchText:(NSString*)searchText
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.getName contains[c] %#",searchText];
_filteredGarages = [NSMutableArray arrayWithArray:[_allGarages filteredArrayUsingPredicate:predicate]];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[_filteredGarages removeAllObjects];
if([searchText length] != 0) {
[self filterContentForSearchText:searchText];
}
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
isSearching = NO;
[self.tableView reloadData];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
if([searchBar.text isEqualToString:#""])
isSearching = NO;
else isSearching = YES;
[self.tableView reloadData];
}
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
if([searchBar.text isEqualToString:#""])
isSearching = NO;
else isSearching = YES;
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
resultsArray = [[NSMutableArray alloc] init];
isSearching = NO;
userArray = [[NSMutableArray alloc] init];
userArray = [//GetYourData//];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [resultsArray count];
} else {
return [userArray count];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do you table view cell job here ...
if (tableView == self.searchDisplayController.searchResultsTableView) {
isSearching = YES;
//fill the cell using resultsArray
} else {
//fill the cell using userArray
}
}
Related
How should I use search controller instead of searchDisplayController in tableView method
I have an example code here
if (tableView == self.searchDisplayController.searchResultsTableView)
Hhere I am getting error on this line it says searchDisplayController is deprecated.
#implementation LocationsViewController
{
NSArray *_park;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.searchResult = [NSMutableArray arrayWithCapacity:[self->_park count]];
// Create a new JSONLoader with a local file URL
JSONLoader *jsonLoader = [[JSONLoader alloc] init];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"park" withExtension:#"json"];
// Load the data on a background queue...
// As we are using a local file it's not really necessary, but if we were connecting to an online URL then we'd need it
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_park = [jsonLoader locationsFromJSONFile:url];
// Now that we have the data, reload the table data on the main UI thread
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
});
}
//Just before showing the LocationDetailViewController, set the selected Location object
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
LocationDetailViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.location = [_park objectAtIndex:indexPath.row];
}
#pragma mark - Table View Controller Methods
//UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"LocationCell"];
Location *location = [_park objectAtIndex:indexPath.row];
cell.textLabel.text = location.name;
cell.detailTextLabel.text = location.address;
// cell.detailTextLabel.text = location.ide;
cell.imageView.image = [UIImage imageNamed:#"location_image"];
///////////////////
if (tableView == self.searchDisplayController.searchResultsTableView)
{
cell.textLabel.text = [self.searchResult objectAtIndex:indexPath.row];
}
else
{
cell.textLabel.text = self->_park[indexPath.row];
}
/////////////////
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//////////////////////////////////////////////
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResult count];
}
else
{
return [self->_park count];
}
//////////////////////////////////////////////
return [_park count];
}
/////////////////////////////////////////////////
- (void)filterContentForSearchText:(NSString*)searchText scope: (NSString*)scope
{
[self.searchResult removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#", searchText];
self.searchResult = [NSMutableArray arrayWithArray: [self->_park filteredArrayUsingPredicate:resultPredicate]];
}
-(BOOL)searchDisplayController:(UISearchController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope: [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
#end
I believe you have declared a UISearchController by passing nil as a parameter to searchResultsController. So your same tableView is being used as searchResultsController of UISearchController
like
let searchController = UISearchController(searchResultsController: nil)
or
UISearchController searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
I hope all you want to do now is to distinguish whether UISeacrhController is active or it's your tableView so that you can show different cell for them :)
If so,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"LocationCell"];
Location *location = [_park objectAtIndex:indexPath.row];
cell.textLabel.text = location.name;
cell.detailTextLabel.text = location.address;
// cell.detailTextLabel.text = location.ide;
// cell.imageView.image = [UIImage imageNamed:#"location_image"];
if (searchController.active)
{
cell.textLabel.text = [self.searchResult objectAtIndex:indexPath.row];
}
else
{
cell.textLabel.text = self->_park[indexPath.row];
}
/////////////////
return cell;
}
My answer in comment if (tableView == self.yourSearchController. searchResultsController) would have worked if you had instantiated a different tableView controller and provided as SearchResultsController for UISearchController
But because you have specified nil in your init of UISearchControllersame tableView instance will be used as SearchResultsController. So comparing will result in always yes :)
Rather, you can depend on active property of UISearchController and check in what context tableView is being used and handle it appropriately :)
Hope I made my point clear :) Happy coding :)
Hi in my array there is huge amount of data and I have to display it in UITableView. But the condition here is I have to display only 5 records initially,then once the user scroll down I have to load more records.I tried searching it but didn't get any useful answer please help me and I agree that we have to use ((void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath )
Another way
we can adjust the return value of tableView:numberOfRowsInSection: method, every time we want to insert ten rows, you can plus 5 to the return value.
but i am not understand how to do this please help me
my code:
#import "TableViewController.h"
#interface TableViewController ()
{
UITableView * tableView;
NSUInteger reloads_;
NSMutableArray * MainArray;
int scrollValue;
long dataLimit;
NSArray * yourDataSource;
}
#end
#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MainArray = [[NSMutableArray alloc]initWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",nil];
tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:tableView];
}
#pragma mark - UITableViewDataSource
// number of section(s), now I assume there is only 1 section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)theTableView
{
return 1;
}
// number of row in the section, I assume there is only 1 row
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section
{
return yourDataSource.count;
}
// the cell will be returned to the tableView
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newFriendCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [yourDataSource objectAtIndex:indexPath.row];
return cell;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
[self estimatedTotalData];
}
- (void)estimatedTotalData
{
long currentRow = ((NSIndexPath *)[[tableView indexPathsForVisibleRows] lastObject]).row;
// 25 here is the initial data count
long estimateDataCount = 25;
while (currentRow > estimateDataCount)
{
estimateDataCount+=25;
}
dataLimit = estimateDataCount;
if (dataLimit == currentRow+1)
{
dataLimit+=25;
if (dataLimit != estimateDataCount)
{
//[self requestForData]; or load necessary data
// take not that dataLimit is the total data that must be displayed.
NSArray *yourLocalData = #[#"1", #"2", #"3", #"4", #"5"];
// just add more sample objects
yourDataSource = [self setsLimitForObject: yourLocalData limit: dataLimit];
[tableView reloadData];
}
}
}
- (NSArray *)setsLimitForObject:(id)object limit:(long)limit
{
if ([object isKindOfClass:[NSArray class]])
{
NSMutableArray *tempArray = [object mutableCopy];
NSRange range = NSMakeRange(0, limit);
if (tempArray.count >= range.length)
return [tempArray subarrayWithRange:range];
else
{
NSLog(#"Out of bounds");
return object;
}
} else NSLog(#"Sets Log: Cannot set limit for object %#", [object class]);
return nil;
}
#end
Try this approach, this is more like a paging...
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self estimatedTotalData];
}
- (void)estimatedTotalData
{
long currentRow = ((NSIndexPath *)[[YourTableView indexPathsForVisibleRows] lastObject]).row;
// 25 here is the initial data count
long estimateDataCount = 25;
while (currentRow > estimateDataCount)
{
estimateDataCount+=25;
}
dataLimit = estimateDataCount;
if (dataLimit == currentRow+1)
{
dataLimit+=25;
if (dataLimit != estimateDataCount)
{
//[self requestForData]; or load necessary data
// take not that dataLimit is the total data that must be displayed.
NSArray *yourLocalData = #[#"1", #"2", #"3", #"4", #"5"];
// just add more sample objects
yourDataSource = [self setsLimitForObject: yourLocalData limit: dataLimit];
[yourTable reloadData];
}
}
}
Update:
- (NSArray *)setsLimitForObject:(id)object limit:(long)limit
{
if ([object isKindOfClass:[NSArray class]])
{
NSMutableArray *tempArray = [object mutableCopy];
NSRange range = NSMakeRange(0, limit);
if (tempArray.count >= range.length)
return [tempArray subarrayWithRange:range];
else
{
NSLog(#"Out of bounds");
return object;
}
} else NSLog(#"Sets Log: Cannot set limit for object %#", [object class]);
return nil;
}
You can use below code for your cellForRowAtIndexPath,
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newFriendCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [mainArray objectAtIndex:indexPath.row];
if (indexPath.row % 5 == 0) { // this if loop will get execute at every fifth row
// if you are loading data from API response then call API here and add next set of data into your existing array.
// then just reload your tableview by it's name e.g. [theTableView reloadData];
}
return cell;
}
and for your numberOfRowsInSection, instead of fixed 5 rows, just use mainArray.count like below,
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section
{
return mainArray.count;
}
I have tab in my app, where user can perform search. After user type keywords and tableView populated with results, there a lot of empty rows. I already "clean" unnecessary rows (turned it to [UIColor clearColor]), but now i got an empty space, problem wasn't solved. What i want is, just a table populated with search results, no empty space, no rows (it perfectly fine if there is blank space, when there is only several results on a table. But i got this "empty" space when there is many results, and i have to scroll down to see all of them. In that case, i didn't want that anything appears after last cell). There is a code, i use to create UISearch and tableView, it pretty straightforward:
For UITableView:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"number of rows is ? %d", self.filteredArray.count);
return [self.filteredArray 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];
}
NSString *_cellText = self.filteredArray[indexPath.row];
cell.textLabel.text = _cellText;
return cell;
}
For UISearch:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
self.filteredArray = [NSMutableArray new];
self.searchResults = [NSArray new];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
self.searchResults = [self._placeOfObjects filteredArrayUsingPredicate:resultPredicate];
NSLog(#" metod called ");
NSLog(#"searchResults is %#", self.searchResults);
// if (self.searchResults){
// _filteredName = ((placeHolder *)(self.searchResults)).name;
// NSLog(#"HOLY Grale %#", _filteredName);
// }
// NSLog(#" What user search is %#", ((placeHolder *)(self.searchResults.name));
if (self.searchResults.count > 0){
// _filteredName = ((placeHolder *)(self.searchResults[0])).name;
for (int i=0; i<[self.searchResults count]; i++) {
NSLog(#" counto %d", [self.searchResults count]);
_filteredName = ((PlaceHolder *)(self.searchResults[i])).name;
[self.filteredArray addObject:_filteredName];
}
}
else {
NSLog(#"array is empty");
}
NSLog(#"HOLY WATER %#", _filteredName );
NSLog(#" %# holy array", self.filteredArray);
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
// Tells the table data source to reload when text changes
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
// Tells the table data source to reload when scope bar selection changes
[self filterContentForSearchText:self.searchDisplayController.searchBar.text scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView;
{
UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 1)];
footer.backgroundColor = [UIColor clearColor];
[tableView setTableFooterView:footer];
}
Any advice would be appreciated, thanks!
I am using xcode 5 and want to search a tableView data. I used "Search Bar & Search Display Controller" for "searching", "CustomCell" for "tableCell" and all the data are parsing from a remote server by NSXMLParsing. All data are showing with their corresponding image without any problem but some how my search option doesn't work properly. I do the same code for a single array searsing and it was working. But here it isn't. At the time of searching it is crashed. Here is my code:
-(void)loadData
{
countryParser = [[CountryParser alloc] loadXMLByURL:#"http://www.avowstudio.com/iOS/PartProject/iOS7/XMLParsingWithCustomeCell/XMLFiles/XMLParsingWithCustomCell.xml"];
countryArray = [countryParser countryArray];
displayItems = [[NSMutableArray alloc] initWithArray:countryArray];
[self.countryTableView reloadData];
[activityIndicator stopAnimating];
[self loadArray];
}
-(void) loadArray
{
CountryInfo *currentCountryOne = [[CountryInfo alloc] init];
totalArray = [[NSMutableArray alloc] init];
for (int i=0; i < [countryArray count]; i++)
{
currentCountryOne = [countryArray objectAtIndex:i];
[totalArray addObject:currentCountryOne.countryName];
}
}
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if ([searchText length] == 0)
{
[displayItems removeAllObjects];
[displayItems addObjectsFromArray:countryArray];
}
else
{
[displayItems removeAllObjects];
displayItems = [[NSMutableArray alloc] init];
for (NSString *string in totalArray)
{
NSRange stringRang = [string rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (stringRang.location != NSNotFound)
{
[displayItems addObject:string];
}
}
}
[self.countryTableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [displayItems count];
}
// This method is kept the "tableRow height" after "done searching"
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
tableView.rowHeight = 100;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CountryCustomCell *Cell = [self.countryTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!Cell)
{
Cell = [[CountryCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
currentCountry = [displayItems objectAtIndex:indexPath.row];
Cell.ContinentLabel.text = currentCountry.continent;
Cell.CountryNameLabel.text = currentCountry.countryName;
Cell.CapitalLabel.text = currentCountry.capital;
imageQueueCountryFlag = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(imageQueueCountryFlag, ^
{
UIImage *imageCountryFlag = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:[currentCountry countryFlag]]]];
dispatch_async(dispatch_get_main_queue(), ^
{
Cell.CountryFlagImageView.image = imageCountryFlag;
[Cell setNeedsLayout];
});
});
return Cell;
}
I don't want to use two Array in "numberOfRowsInSection" & "cellForRowAtIndexPath" with flag or some thing like that, to avoid more coding. If any one familiar with it please share that here. A lot of thanks in advanced.
I think you are added objects of CountryInfo in the [countryParser countryArray]. In your loadArray method, you just initializing a blank object currentCountryOne and each time the for loop executes, trying to add a blank object's property into totalArray. Please make changes as below:
-(void) loadArray
{
totalArray = countryArray;
}
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if ([searchText length] == 0)
{
[displayItems removeAllObjects];
[displayItems addObjectsFromArray:countryArray];
}
else
{
[displayItems removeAllObjects];
displayItems = [[NSMutableArray alloc] init];
for (CountryInfo *country in totalArray)
{
NSRange stringRang = [country.countryName rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (stringRang.location != NSNotFound)
{
[displayItems addObject:country];
}
}
}
[self.countryTableView reloadData];
}
Instead of adding string to displayItems add the objects containing information about country.
[displayItems addObject:string];
Here you are adding only the countryName to displayItems but while calling cellForRowAtIndexPath: you are asking for its continent,country name and capital which is not present in the array (displayItems) when you reload table after searching.
Exactly which error did you get when it crashed?
It seems to be that when you add an object to your displayItems array. You just add the string object, not the country object.
Your currentCountry is not a country object just the string.
So, in your code enter:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
When you try to parse, it cannot reach:
continent
countryName
capital
Be careful when you search a detailed object.
when you search you add strings to display items not CountryInfo objects so when cellforrow atindexpath gets called again it has to crash
I'm getting an error trying to use a UISearchBar to filter objects from an array that populate a UITableView. The error states: Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 3 beyond bounds [0 .. 2]'.
The relevant code in ViewController.m is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (isFiltered == YES)
{
NSLog (#"%i", indexPath.row);
cell.textLabel.text = [filteredCities objectAtIndex:indexPath.row];
}
else
{
cell.textLabel.text = [initialCities objectAtIndex:indexPath.row];
}
return cell;
}
#pragma mark - UISearchBar Delegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length == 0)
{
isFiltered = NO;
}
else
{
isFiltered = YES;
filteredCities = [[NSMutableArray alloc] init];
for (NSString *cityName in initialCities)
{
NSRange cityNameRange = [cityName rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (cityNameRange.location != NSNotFound)
{
[filteredCities addObject:cityName];
}
}
}
[myTableView reloadData];
}
#end
You should also change tableView:numberOfRowsInSection method to something like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (isFiltered) {
return [filteredCities count]; /// Filtered items
}
return [initialCities count]; /// All items
}
there is a better way to do this. you should use a search bar controller. and use these two delegate methods.
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.property contains[cd] %#",
searchText];
self.searchResults = [self.datasource filteredArrayUsingPredicate:resultPredicate];
}
// code to refresh the table with the users search criteria
// this gets called everytime the user search string changes.
// also it calls the function above.
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
Now in your table view. you can refer to the new table the search bar controller creates by saying
self.searchBarController.searchResultsTable
and you can put this in the if else and check to see the table rather than using an isFiltered property.
if (tableView == self.searchDisplayController.searchResultsTableView){
} else { // your in the other original table