In iOS apps, often we need to implement Indexed List for enabling Index Scrolling in a TableView, just like the contacts view of an iPhone. Indexed List enables fast scrolling to required section without trolling through each and every sections. Since it is required in various apps , it has to be made generic, so that it can be used anywhere required. Just by adding the statements in your model class will work the whole thing out. After this tutorial , your simulator will show output , presented in the following demo.
A basic idea of UITableView and it’s delegate functions are required before begining this project. For more information on UITableView please refer to Apple’s Developer Class Reference for UITableView.
At this point your project structure should look like this.
Go to navigator and click on Statements.h and add the following lines of codes :
@property (strong , nonatomic) NSMutableArray *statement; //an array to store the statements -(NSMutableArray *)initializeStatements; //function to initialize statements
Now we will initialize the above statement array with a list of statements to be displayed on tableview. Synthesize the above array in Statements.m by adding the following code just after @implementation Statements.
@synthesize statement;
Add the following function after above code to initialize the aforesaid array.
-(NSMutableArray *)initializeStatements { statement = [[NSMutableArray alloc]initWithCapacity:0]; [statement addObject:@"your statements"]; //likewise add objects in array. For better checking purpose, remember to have variance among statements while adding them in array. return statement; }
You can get the statements from here.
Now, go to Controller section and delete the files TableViewController.h and TableViewController.m (since they were subclass of UIViewController instead of UITableviewController) and add in same section create new file with same name but with subclass of UITableViewController.
Now go to your Main.storyboard and delete the ViewController over there. Go to the Utilities section and drag UITableViewController to your storyboard.
After that, under utilities section, go to the attributes inspector and set the custom class as TableViewController.
Add following the following line just below #import “TableViewController.h”
#import "Statements.h"
Now, click on the TableViewController.m file and declare the following private property.
@property (strong , nonatomic) NSMutableArray *statementArray;
an synthesize it using
@synthesize statementArray;
This array will contain all the statements to be displayed on the table. Under viewDidLoad add following statements after [super viewDidLoad]
statementArray = [[NSMutableArray alloc]initWithCapacity:0]; statements *Object=[[Statements alloc]init]; statementArray=[Object initializeStatements]; //will initialize array with statements [statementArray sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; // will sort the array in ascending order [self createAlphabetArray];
The following lines of codes includes allocating an array, creating an instance of class Statements and initializing the allocated array with statements declared within Statements.h file. The second last line of code will arrange the statements in statementsArray automatically in ascending order . The last line of code calls a method for creating an array of alphabets, composed by starting letters of statements. Now, we will implement the method createAlphabetArray. Add following lines after viewDidLoad
-(void)createAlphabetArray { alphabetArray = [[NSMutableArray alloc]initWithCapacity:0]; for (int i=0; i< statementArray.count; i++) { NSString *firstletter=[[statementArray objectAtIndex:i]substringToIndex:1]; //modifying the statement to first letter if (![alphabetArray containsObject:firstletter]) //checking the array if the modified statement already exists in array { [alphabetArray addObject:firstletter]; } } [alphabetArray sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; //sorting array in ascending array }
This method will create an array of alphabets , alphabets being taken from first letter of statements and sorted in ascending order. Now we will implement the methods under #pragma mark – Table view data source .
Under the function -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView remove all codes and add the following line of code for setting the total number of sections displayed in table.
return alphabetArray.count;
Now, in function -(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section add the following code to set title for each section.
NSString *title; for (int i=0; i<alphabetArray.count; i++) { if (section==i) { title= [alphabetArray objectAtIndex:i]; } } return title;
Add the following function which will take a section as input and return an Array of row contents.
-(NSMutableArray *)getArrayOfRowsForSection:(NSInteger)section { NSString *rowTitle; NSString *sectionTitle; NSMutableArray *rowContainer=[[NSMutableArray alloc]initWithCapacity:0]; for (int i=0; i<alphabetArray.count; i++) { if (section==i) // check for right section { sectionTitle= [alphabetArray objectAtIndex:i]; //getting section title for (NSString *title in statementArray) { rowTitle=[title substringToIndex:1]; //modifying the statement to its first alphabet if ([rowTitle isEqualToString:sectionTitle]) //checking if modified statement is same as section title { [rowContainer addObject:title]; //adding the row contents of a particular section in array } } } } return rowContainer; }
Now add the following lines of codes to function – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section for setting number of rows for each section.
NSMutableArray* rowArray=[[NSMutableArray alloc]initWithCapacity:0]; rowArray=[self getArrayOfRowsForSection:section]; return rowArray.count;
The only thing left before drawing each cell is setting the title for each row. This will be done under the function -(NSString *)titleForRow:(NSIndexPath *)indexpath which will take a NSIndexPath object as input and return a title as string. Add the following lines of codes to the above function.
NSMutableArray* rowArray=[[NSMutableArray alloc]initWithCapacity:0]; rowArray=[self getArrayOfRowsForSection:indexpath.section]; NSString *titleToBeDisplayed=[rowArray objectAtIndex:indexpath.row]; return titleToBeDisplayed;
Now, the last only necessary implementation for creating a tableview is implementation of -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath function. Add following lines of codes in the above delegate function after UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; and before return cell part.
cell.textLabel.text= [self titleForRow:indexPath]; //getting cell content
After completing this go to your Main.storyboard and then click on the cell appearing on TableViewController . Then set the Identifier with name “Cell” in the attributes inspector under utilities section. Now run your project . Your output will look like this.
The output is somewhat not as desired as we can see the statement but not the full statement. For making the whole sentence visible ad these two lines of codes in the function – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath before return cell part.
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping; //enabling WordWrapping of text in cell cell.textLabel.numberOfLines = 0;
After adding, run your Project .Even though, all statements are completely visible , you might have observed some places where table contents become messy (where statements are composed of 2 or 3 lines).
This is because no padding have been provided between contents and cell border where contents become large i.e. of 2-3 lines. So, to provide some extra padding we will implement function -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
Add following codes to above function.
NSMutableArray* rowArray=[[NSMutableArray alloc]initWithCapacity:0]; rowArray=[self getArrayOfRowsForSection:indexPath.section]; NSString *cellText =[rowArray objectAtIndex:indexPath.row]; //get row content of cell UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:12.0]; // use fontName and fontSize same as that you used in storyboard CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT); //will calculate labelSize for each cell required according to content CGSize labelSize = [cellText boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:cellFont} context:nil].size; /* In case you are using Xcode 4.0 use function CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping]; */ CGFloat newHeight; if (labelSize.height+20 > 40) // providing a padding of 10 below and 10 above { newHeight= labelSize.height+20; } else { newHeight= 40; } return newHeight;
Now run your project . iOS simulator will look somewhat like this, which looks preferably neat and proper.
Now, let’s start implementing indexed scrolling in tableview.
For this , we are going to implement two functions. The very first function is – (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView. This function will return the alphabets that are going to be present in the index scroller, i.e. the alphabetArray.
Add the following line in the above function.
return alphabetArray;
After forming the array we will implement the scrolling of tableview with reference to the alphabet touched on scroller. For this we will implement the function – (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index . Add the following lines of code in above function.
NSIndexPath *indexpath; for (int i=0; i < alphabetArray.count; i++) { NSString *titleToSearch=[alphabetArray objectAtIndex:i]; //getting sectiontitle from array if ([title isEqualToString:titleToSearch]) // checking if title from tableview and sectiontitle are same { indexpath=[NSIndexPath indexPathForRow:0 inSection:i]; // scrolling the tableview to required section [self.tableView scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionTop animated:YES]; break; } } return indexpath.section;
Now run your project . Your project will look like this. and will function same as what you saw in the starting demo except for deleting and adding statements in tableview which are yet to be implemented. For deleting rows from tableview, we have to implement its delegate function -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath. Add following lines of code for deleting rows in above function.
NSString *statementToBeDeleted=nil; NSMutableArray* rowArray=[[NSMutableArray alloc]initWithCapacity:0]; if (editingStyle == UITableViewCellEditingStyleDelete) { rowArray=[self getArrayOfRowsForSection:indexPath.section]; //getting the row content for a section statementToBeDeleted=[rowArray objectAtIndex:indexPath.row]; //getting the object to be deleted //removing object from the original content array [statementArray removeObject:statementToBeDeleted]; //deleting content from tableview [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; //again creating the alphabet array for section titles and index scroller [self createAlphabetArray]; //reloading the tableview for preventing project from crashing [[self tableView]reloadData]; }
Run your project and check for your delete operation if working or not. It should function like the starting demo except for add operation .
For implementing add operation in tableview, go to your storyboard and add a Navigation Bar to your tableview. To the Navigation Bar add a Bar Button Item.
For references on how to add Navigation Bar on UITableview, check this video tutorial
Set the title of Navigation Bar to “Statement Table”.
Now, ctrl-drag from that add button to the implementation file TableViewController.m and create a method addNewStatement for implementing an additional view which will be used for adding statements.
Now under this function add the following lines of codes which will create a UIView with animation , a UITextView and two buttons , Save and Cancel on the same UIView programmatically.
self.tableView.scrollEnabled=NO; //disabling scroll of tableview //Creating a UIView on which whole functionality is to be implemented CGRect statementAdderFrame=CGRectMake(20, -480, 280, 440); statementAddingView=[[UIView alloc]initWithFrame:statementAdderFrame]; statementAddingView.backgroundColor=[UIColor whiteColor]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.4]; statementAddingView.frame=CGRectMake(20,20, 280, 440); translucentView=[[UIView alloc]initWithFrame:CGRectMake(0,0, 320, 480)]; translucentView.backgroundColor=[UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:0.7]; [self.view.window addSubview:translucentView]; [translucentView addSubview:statementAddingView]; // creating a UILabel for Enter Statement label CGRect enterStatementLabelFrame= CGRectMake(20, 20, 240, 40); UILabel *enterStatementLabel=[[UILabel alloc]initWithFrame:enterStatementLabelFrame]; enterStatementLabel.backgroundColor=[UIColor clearColor]; [enterStatementLabel setFont:[UIFont fontWithName:@"Helvetica" size:16]]; enterStatementLabel.text=@"Enter Statement:"; enterStatementLabel.userInteractionEnabled=NO; enterStatementLabel.textAlignment=NSTextAlignmentLeft; [statementAddingView addSubview:enterStatementLabel]; //cretaing a UITextView for entering statement to be added CGRect statementTextViewFrame=CGRectMake(20, 85, 240,130); statementTextView=[[UITextView alloc]initWithFrame:statementTextViewFrame]; statementTextView.backgroundColor=[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.3]; statementTextView.textColor=[UIColor blackColor]; [statementTextView setFont:[UIFont fontWithName:@"Helvetica" size:15]]; [statementAddingView addSubview:statementTextView]; [UIView commitAnimations]; //creating add button for adding statements to tableview UIButton *addButton=[UIButton buttonWithType:UIButtonTypeCustom]; addButton.frame=CGRectMake(20, 365, 70, 30); [addButton setTitle:@"Save" forState:UIControlStateNormal]; addButton.tag=1; [addButton setBackgroundColor:[UIColor colorWithRed:85.0/255.0 green:255.0 blue:85.0/255.0 alpha:1.0]]; [addButton.titleLabel setFont:[UIFont fontWithName:@"Helvetica-Bold" size:16]]; [addButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; //declaring the selector and target for the Save button [addButton addTarget:self action:@selector(statementAdded:) forControlEvents:UIControlEventTouchUpInside]; [self.statementAddingView addSubview:addButton]; //creating cancel button for cancelling whole operation UIButton *cancelButton=[UIButton buttonWithType:UIButtonTypeCustom]; cancelButton.frame=CGRectMake(190, 365, 70, 30); [cancelButton setTitle:@"Cancel" forState:UIControlStateNormal]; cancelButton.tag=2; [cancelButton setBackgroundColor:[UIColor colorWithRed:255.0 green:85.0/255.0 blue:85.0/255.0 alpha:1.0]]; [cancelButton.titleLabel setFont:[UIFont fontWithName:@"Helvetica-Bold" size:16]]; [cancelButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; //declaring the selector and target for the Cancel button [cancelButton addTarget:self action:@selector(statementAdded:) forControlEvents:UIControlEventTouchUpInside]; [self.statementAddingView addSubview:cancelButton]; // a tap gesture for hiding the keyboard when entering of statement is over UITapGestureRecognizer *tapOnView=[[UITapGestureRecognizer alloc]init]; [tapOnView addTarget:self action:@selector(hideKeyboard:)]; //declaring the selector for tap gesture [tapOnView setNumberOfTapsRequired:1]; [tapOnView setNumberOfTouchesRequired:1]; tapOnView.enabled=YES; [statementAddingView addGestureRecognizer:tapOnView];
Lets, implement the selector declared for both buttons and the tap gesture. Add the following function to your program for implementing selector of tap gesture.
-(void)hideKeyboard:(UITapGestureRecognizer *)sender { //hide keybaord [statementTextView resignFirstResponder]; }
After the above function add the following function to implement the selectors for the two buttons, Save and Cancel , i.e. -(void)statementAdded:(UIButton *)sender
if (sender.tag==1) //checking if button was "Save" button { if ([statementTextView.text isEqualToString:@""] ) // if no statement is added to text view { UIAlertView *notAddedAlert=[[UIAlertView alloc]initWithTitle:@"Error" message:@"No statement to add" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; [notAddedAlert show]; } else { //providing animations for release of the UIView [UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionTransitionNone animations:^{ statementAddingView.frame=CGRectMake(20,-480, 280, 440); } completion:^(BOOL finished) //completion handler for animation { //adding codes which are to be implemented after animation is over self.tableView.scrollEnabled=YES; //enabling scroll of UIView [translucentView removeFromSuperview]; NSString *statementToAdd=self.statementTextView.text; [statementArray addObject:statementToAdd]; //adding statement to original array [statementArray sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; //sorting the array [self createAlphabetArray]; // creating alphabet array for section title and index scroller [self.tableView reloadData]; //reloading the tableview for updation //code for scrolling of tableview to the position where statement is added NSMutableArray *rowContainer=[[NSMutableArray alloc]initWithCapacity:0]; NSString *sectionString=[statementToAdd substringToIndex:1]; NSInteger sectionIndex=[alphabetArray indexOfObject:sectionString]; for (NSString *title in statementArray) { NSString *rowTitle=[title substringToIndex:1]; if ([rowTitle isEqualToString:sectionString]) { [rowContainer addObject:title]; } } NSInteger rowIndex=rowContainer.count-1; [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex] atScrollPosition:UITableViewScrollPositionTop animated:YES]; }]; [UIView commitAnimations]; } } else // if the button is "Cancel" button { // providing animations for release of UIView [UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionTransitionNone animations:^{ statementAddingView.frame=CGRectMake(20,-480, 280, 440); } completion:^(BOOL finished) //completion handler for animation. { //adding codes which are to be executed after animation is oover self.tableView.scrollEnabled=YES; // enabling scroll of tableview [translucentView removeFromSuperview]; }]; [UIView commitAnimations]; }
Your project is complete and now press ctrl+R to run it. Your simulator output will be somewhat like this.
You can download the complete project from here.