Adding a search bar to iPhone application that uses alphabetical sections? - iphone

I have an application that uses a UITableView which contains the names of products, these products are also split up into their respective sections based on their first letter.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
//Initialize alphabet array
m_Alphabet = [[NSArray alloc]initWithObjects:#"A",#"B",#"C",#"D",#"E",#"F",#"G",#"H",#"I",#"J",#"K", #"L",#"M",#"N",#"O",#"P",#"Q",#"R",#"S",#"T",#"U",#"V",#"W",#"X",#"Y",#"Z",#"Other", nil];
//Initialize alphabet distionary of arrays
m_AlphabetDictionary = [[NSMutableArray alloc]init];
//Populate distionary with a mutable array for each character
//in the alphabet (plus one "Other" category)
for (int i = 0; i < 27; i++)
[m_AlphabetDictionary insertObject:[[NSMutableArray alloc] init] atIndex: i];
// The number of products in the database
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
//For each product in the appDelgates products
for (Product *product in appDelegate.m_Products){
if ([product.category isEqualToString:productType]){
[tempArray addObject:product];
//firstLetter is equal to the first letter of the products name
NSString * l_FirstLetter = [product.name substringToIndex:1];
//convert firstString to uppercase
l_FirstLetter = [l_FirstLetter uppercaseString];
//The added flag ensures objects whose first letter isn't a letter
//are added to array 26
bool added = NO;
for(int i=0; i<[m_Alphabet count]; i++){
//If the first letter of product name is equal to the letter at index i in theAlphabet
if ([l_FirstLetter isEqualToString:[m_Alphabet objectAtIndex:i]]) {
//Add product to section array for that letter
[[m_AlphabetDictionary objectAtIndex:i] addObject:product];
added = YES;
}
}
//If product hasn't been added to array, add it to "Others" category
if(!added)
[[m_AlphabetDictionary objectAtIndex:26] addObject:product];
}
}
//Set navigation controller title
self.title = productType;
}
//Number of sections is equal to the length of the m_Alphabet array
//Letters A-Z plus "Other"
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [m_Alphabet count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[m_AlphabetDictionary objectAtIndex:section] count];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return m_Alphabet;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [m_Alphabet indexOfObject:title];
}
- (NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section
{
if ([[m_AlphabetDictionary objectAtIndex:section] count]==0)
return nil;
return [m_Alphabet objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Cell for row");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if([self.productType isEqualToString:#"All"]){
Product *product = (Product *) [appDelegate.m_Products objectAtIndex:indexPath.row];
cell.textLabel.text = product.name;
// Configure the cell.
return cell;
}
else {
//Instead of using appDelegate.products use the new array that will be filled
//by the numberOfReowsInSection method
Product *product = (Product *)[[m_AlphabetDictionary objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
cell.textLabel.text = product.name;
// Configure the cell.
return cell;
}
}
What I'm looking to do is add a search bar to the top of my table view that behaves just like the search bar in the "All Contacts" section in the iPhones contacts application. I.E. When I search, all the sections disappear and just the search results are displayed until the search bar is blank again.
Can anyone point me in the right direction?
Thanks,
Jack

Looks like you need to uses UISearchDisplayController, which is most likely what the contacts application uses (along with mail, maps, safari, and music). It presents itself as a table view overlay that may or may not contain a scope bar (your choice) and filters results based on the search bar's text. A simple tutorial involving interface builder may be found here, and an apple example sans-IB may be found here.

Related

How To Fetch the value Section wise in UItableview?

I am trying to make an app in which i use the Grouped Table view .In that I am creating the sections in which the first letter comes from an array and it is doing perfectly.
the code is below
sections=[[NSMutableArray alloc] init];
for(int i=0;i<[PdfNames count]; i++)
{
NSString *s=[[PdfNames objectAtIndex:i] substringToIndex:1];
NSPredicate *p=[NSPredicate predicateWithFormat:#"letter like '%#'",s];
NSArray *check=[sections filteredArrayUsingPredicate:p];
if([check count]<1)
{
dict=[[[NSMutableDictionary alloc] init] autorelease];
[dict setValue:s forKey:#"letter"];
[sections addObject:dict];
}
}
But now i am not able to get that hoe can i get the names of pdf from array which belongs to their secion Or starts with that alphabet.
You are creating an array sections which is of course empty. You then create an array check based on sections. It will therefore also be empty. [check count] will always be zero. No NSDictionary is going to be created.
Clear?
Try following code its basic logic, I hope you can understand.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section == 0) {
return 10; // Number of rows in section.
} else if (section == 1) {
return 5;
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2; // Let say we have two section.
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
if (indexPath.section == 0) {
// Assign your data
} else if (section == 1) {
// Assign your data
}
}
Similarly you can check for the table section on didSelectRowAtIndexPath: method

how to give section alphabetical order in iphone

I have table and one global variable in appdelegate class. I store all date in this global array and showing this array value in controller in table view. I have to give section index with alphabetical order by short how to give please help me on this
I tried this code but it not working for me
Kindly help me.
h.file:
NSMutableArray *timeZonesArray;
NSMutableArray *sectionsArray;
UILocalizedIndexedCollation *collation;
.mfile
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
//return 1;
return [[collation sectionTitles] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
//app.networkActivityIndicatorVisible = YES;
NSArray *timeZonesInSection = [app.lstAirports objectAtIndex:section];
return [timeZonesInSection count];
// return app.lstAirports.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:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
NSArray *timeZonesInSection = [app.lstAirports objectAtIndex:indexPath.section];
a=(airport*)[timeZonesInSection objectAtIndex:indexPath.row];
NSLog(#"str:%#",a);
if(a!=nil){
cell.textLabel.text =a.Name;
cell.detailTextLabel.text=a.Code;
//[app.spinner stopAnimating];
}
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[collation sectionTitles] objectAtIndex:section];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [collation sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [collation sectionForSectionIndexTitleAtIndex:index];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
app.originAirport = (airport*)[app.lstAirports objectAtIndex:indexPath.row];
[delegate Originstart:app.originAirport forIndexPath:self.indexPathToChange];
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark Set the data array and configure the section data
- (void)setTimeZonesArray:(NSMutableArray *)newDataArray {
if (newDataArray != timeZonesArray) {
[timeZonesArray release];
timeZonesArray = [newDataArray retain];
}
if (timeZonesArray == nil) {
self.sectionsArray = nil;
}
}
In order to display/arrange your data in alphabetical which in a array you have to use NSSortDescriptor
you have to make the object of this NSSortDescriptor class and give data here which you are fetching from XML
NSSortDescriptor *itemXml = [[NSSortDescriptor alloc] initWithKey:#"itemName" ascending:YES];
Now suppose you have an array sortDescriptors
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:itemXml,nil];
[yourArray sortUsingDescriptors:sortDescriptors];
I hope this will be usefull to you.
If you are looking for section-wise sorting I would hold your various sections in a dictionary that holds arrays for each of the sections and then sort each of those arrays as needed. It requires a bit more code on the table delegate methods but allows for complete control over each section's data
Another option is to encapsulate your data in a different and add a property for the section you want it in and add another sort descriptor to that so it is sorting the array by section and then by any other value. You do that by just adding another NSSortDescriptor to the array in the order you want the array to be sorted by.
The perfect example , please refer the code,
Sorting Alphabetically

Problem with sectioning UITableViewController

I have a class that extends UITableviewController which displays a data type called "GigData" (which only contains strings for now). The content is stored in "data" which is an NSMutableArray containing NSMutableArrays containing "GigData". This array is passed to the instance of my class and the arrays inside arrays make up the sections of the table. Here is the code I have implemented so far:
#synthesize data = _data;
- (id)init
{
self = [super initWithStyle:UITableViewStyleGrouped];
_data = [[NSMutableArray alloc] init];
[[self navigationItem] setTitle:#"Gigs by Date"];
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
return [self init];
}
- (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
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [_data count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[_data objectAtIndex:section] 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];
}
NSMutableArray *sectionArray = [_data objectAtIndex:[indexPath section]];
GigData *gig =[sectionArray objectAtIndex:[indexPath row]];
[[cell textLabel] setText:[gig description]];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
GigData *temp = [[_data objectAtIndex:section] objectAtIndex:0];
return [temp date];
}
When I run the app, I can see everything sorted into the right groups and all the displays are correct, except for the final section, which keeps changing names, some of which have included "cs.lproj", "headers" and "method not allowed". Scrolling to the bottom of the table then towards the top crashes the app. Also, if I provide my own implementation for description for "GigData", the app crashes even worse, I cannot scroll to the second section at all. Data is declared as a property in the header file and is set to nonatomic and retain. I have also tried using the code inside the init method to create the data array inside this class, but this makes no difference. Some runnings of the app have said there is a problem in tableView:cellForRowAtIndexPath: when I create "sectionArray". Has any body got any suggestions as to what I am doing wrong?

Hiding UITableViewCell

Is there a way to hide a UITableView cell? I'm looking for some property or method I can invoke on the UITableViewCell returned by a synchronous cellForRowAtIndexPath() to hide it and make it unselectable by the user.
For me using mapping is not easy way, so I decided to use SAS method. But it doesn't work with my custom cell. So, I correct it:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 7 && hide7Row){
UITableViewCell* cell = [cells objectAtIndex:indexPath.row];
cell.hidden = YES;
return 0.0;
}
else if(indexPath.row == 8 && hide8Row){
UITableViewCell* cell = [cells objectAtIndex:indexPath.row];
cell.hidden = YES;
return 0.0;
}
else {
return 44.0;
}
}
Works Fine.
You mean to leave a gap in the table where the cell should be, or just to progress from the one before it straight to the one after it? In the former case, I guess you might try getting the cell's contentView and set its hidden property to YES; otherwise, you'll just have to do a little logic in your -tableView:numberOfRowsInSection: and -tableView:cellForRowAtIndexPath: methods, returning (the number of cells you'd otherwise return - 1) from the first, and, depending on whether the row index is less than or greater than the row you're not including, either (the cell you'd otherwise return) or (the cell at (the row index + 1)), respectively.
(edit, because the explanation was convoluted:)
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
if(section == theSectionWithoutARow)
{
if(shouldRemoveTheRow)
return [theArrayWithTheSectionContents count] - 1;
else
return [theArrayWithTheSectionContents count];
}
// other sections, whatever
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// blah blah standard table cell creation
id theCellObject;
if(indexPath.section == theSectionWithoutARow)
{
NSInteger theActualRowToDisplay = indexPath.row;
if(shouldRemoveTheRow && indexPath.row >= theRowIndexToRemove)
{
theActualRowToDisplay = indexPath.row + 1;
}
theCellObject = [theArrayWithTheSectionContents objectAtIndex:theActualRowToDisplay];
}
// now set up the cell with theCellObject
return cell;
}
There is no method to do that on the cellForRowAtIndexPath as far as I am aware.
Noah Witherspoon's method seems to be more or less workable, although it will need to be modified if you want multiple rows to be hidden.
Another way to approach it is to create a "cell map", I don't know if this is more efficient or not, but I've used it and it worked.
Let us say you have an NSArray (or mutable version thereof) of data which is to be shown in your TableView. The array's count property is used as the return value for your numberOfRowsInSection delegate method. This is a somewhat typical approach to my knowledge.
To make it so that only some of the rows are shown, I created a "mapping array", which is an NSMutableArray that contains "pointers" to your actual data array. The map contains integers wrapped in NSNumbers. In its virgin state the map's index 0 has the integer 0 (wrapped in NSNumber), index 1 has integer 1, etc.
The UITableView delegate methods are built so that the map's index count is used for numberOfRowsInSection. In the cellForRowAtIndexPath method, it looks at the appropriate index of the map array, retrieves whatever is wrapped in the NSNumber, and then looks in that index of your actual data array.
The benefit of this dereference is that it becomes extremely easy to add and remove cells from the table. Just add/remove the NSNumber objects from your mapping array. Make sense? Sorry, not at my Mac or I could just put up some code samples.
Oh, and don't forget that you have to call the update method (the exact name escapes me) on your TableView so that it refreshes and the cells hide/unhide.
Had the same problem, and as I wanted to avoid some mapping as mentioned, I just set the cell-size to 0:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row=[indexPath row];
float ret=0.0;
if( row==3) {
ret=0.0;
}
else {
ret=40.0;
}
return ret;
}
I used Matt's technique to create a mapping to cell data. Here is some code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return computeNumberOfRowsAndMapCellData;
}
// Compute mapToCellData to map the index of the cell to the cell data for the
// cell based on TaskConfig show/hide.
// Return the number of rows in section 1.
- (NSInteger)computeNumberOfRowsAndMapCellData {
if (mapToCellData) {
[mapToCellData release];
}
mapToCellData =[[NSMutableArray alloc] init];
if (cellData) {
[cellData release];
}
cellData = [[NSMutableArray alloc] init];
NSInteger numberOfRows = 12; // maximum number of rows
NSNumber *index = [NSNumber numberWithInt:0];
// If the data is not configured to show, decrement the number of rows in the table.
if ( ! [configManager isShowDateForType:task.case_type severity:task.severity]) {
numberOfRows--;
} else {
// Add a map to the cell data with the row number.
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[df stringFromDate:task.respond_due_date], #"Respond Due", nil];
[cellData addObject:dict];
[mapToCellData addObject:index];
int value = [index intValue];
index = [NSNumber numberWithInt:value + 1];
}
// Check the configuration for the rest of the rows of cell data.
....
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:15];
cell.detailTextLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:14];
}
NSUInteger mapIndex = 0;
// Use the mapToCellData to find cell data based on show/hide in ConfigManager for the data type.
mapIndex = [indexPath row];
NSNumber *cellDataIndex = [mapToCellData objectAtIndex:mapIndex];
NSDictionary *cellDataDict = [cellData objectAtIndex:[cellDataIndex unsignedIntegerValue]];
cell.textLabel.text = = [[cellDataDict allValues] objectAtIndex:0];
cell.detailTextLabel.text = [[cellDataDict allKeys] objectAtIndex:0];

how to control the index displayed on the tableview

I am able to show a index on the right side similar to songs view on the ipod. During searching the index bar gets minimized automatically. And when i go back to my actual table view the index size is small and it displays only few alphabets. How to stop the resizing of this ?
you have to place proper delegate methods of UITableView in your viewController.m file.
for example I have placed following code.
Please read comments carefully.
#pragma mark -
#pragma mark Table view data source
// an array count which has values for index - like A,B,C etc.
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//return [NSArray arrayWithArray:keys];
return [keys count];
}
// an array which has values for index - like A,B,C etc.
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
return keys;
}
// return how many number of rows are required for each section.
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[dMain valueForKey:[keys objectAtIndex:section]] count];
}
// return title of section
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return [keys objectAtIndex:section];
}
// create each row for different sections
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = [NSString stringWithFormat:#"%i %i",indexPath.row,indexPath.section];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.font=[UIFont fontWithName:#"Helvetica" size:12];
//cell.textLabel.text=[[[objects objectAtIndex:indexPath.row] valueForKey:#"marketname"] stringByReplacingOccurrencesOfString:#"_and_" withString:#"&"];
cell.textLabel.text=[[[[dMain valueForKey:[keys objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row] valueForKey:#"marketname"] stringByReplacingOccurrencesOfString:#"_and_" withString:#"&"];
}
return cell;
}