Tab bar controllers are used when we need to see more than one view of the same data. In this app, we’ll implement a simple version of an indexing utility, which counts the words in a text sample and orders them by their frequency. The source code for this example can be found in the fileĀ TabbedAppIOS7.
Create a new application in Xcode using the Tabbed Application template. Give the project a name, make sure it is set for iPhone, and save it by clicking Create.
Open Main.storyboard. Replace the second view controller of the tab bar controller with a UINavigationController (and its accompanying UITableViewController). When you are done with this step, the storyboard should look like this:
We’ll keep the FirstViewController class, but delete SecondViewController. We’ll be replacing it with a subclass of UITableViewController soon. Go ahead and delete SecondViewController .h and .m, and add a new class named WordCountViewController. Make sure that the table view controller in the storyboard has its class set to WordCountViewController:
Here is a detail of the FirstViewController’s view:
The view for the FirstViewController contains a label and a UITextView as shown above. There is also a view-sized custom button (with no text) that is used to dismiss the keyboard on the text view. Edit FirstViewController.h so that it looks like this:
#import "WordCountViewController.h" @interface FirstViewController : UIViewController @property (nonatomic, weak) IBOutlet UITextView *textView; @property (nonatomic, strong) NSCountedSet *uniqueWords; - (IBAction)processText:(UIButton *)sender; @end
There are two properties. The first is the outlet to the text view, the second is an NSCountedSet that will be used to count instances of unique words in the string the user enters in the text view. FirstViewController.m implements the processText method:
#import "FirstViewController.h" @implementation FirstViewController - (NSCountedSet *)uniqueWords { if (!_uniqueWords) { _uniqueWords = [[NSCountedSet alloc] init]; } return _uniqueWords; } - (IBAction)processText:(UIButton *)sender { [self.textView resignFirstResponder]; NSRange textViewRange = NSMakeRange(0, [self.textView.text length]); [self.uniqueWords removeAllObjects]; [self.textView.text enumerateSubstringsInRange:textViewRange options:NSStringEnumerationByWords | NSStringEnumerationLocalized usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { [self.uniqueWords addObject:[substring lowercaseString]]; }]; UINavigationController *navController = (UINavigationController *)[self.tabBarController.viewControllers objectAtIndex:1]; WordCountViewController *wcVC = (WordCountViewController *)[navController.viewControllers objectAtIndex:0]; wcVC.wordSet = self.uniqueWords; } @end
We first lazily instantiate uniqueWords as an empty NSCountedSet. In processText, we resign the first responder, which dismisses the keyboard on the text view. We then set up an NSRange that is equal to the length of the entire text. Next, we make sure that uniqueWords is empty: this may not be the first time that words are being counted!
The enumerateSubstringsInRange: options: usingBlock: method takes as its parameters:
- The range on which to operate
- An “ORed” list of options (here, NSStringEnumerationByWords and NSStringEnumerationLocalized)
- A block that performs the actual processing.
In the block, we simply add the current word in the enumeration to the uniqueWords set as a lowercase string.
Now we need to get the words into the WordCountViewController to be viewed as a list (in a table view). We get the navigation controller by getting the view controller at index 1 from the table view controller, then get the table view controller by getting the navigation controller’s view controller at index 0. We must cast this controller as a WordCountViewController in order to set the wordSet property.
Here is WordCountViewController.m:
#import "WordCountViewController.h" @interface WordCountViewController () @end @implementation WordCountViewController - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView reloadData]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.wordSet.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // Configure the cell... NSArray *sortedArray = [self.wordSet.allObjects sortedArrayUsingComparator:^(id obj1, id obj2) { NSUInteger first = [self.wordSet countForObject:obj1]; NSUInteger second = [self.wordSet countForObject:obj2]; NSComparisonResult retVal; if (first < second) { retVal = NSOrderedDescending; } else if (first == second) { retVal = NSOrderedSame; } else { retVal = NSOrderedAscending; } return retVal; }]; cell.textLabel.text = [sortedArray objectAtIndex:indexPath.row]; cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", [self.wordSet countForObject:cell.textLabel.text]]; return cell; } @end
Most of this code is standard code for dealing with table views. The important code in this example is in tableView: cellForRowAtIndexPath:, where we sort the entries before displaying them. This is done using the sortedArrayUsingComparator: method of NSArray (we get the array by taking allObjects of the set). The comparator works by looking at the counts for each word, then returning an appropriate NSOrdering flag for each pair of objects in the array to be sorted. This will sort the objects by their count, descending. We display the word and the count for the word in the table view cell.
Here is a test run of the app: