How to Implement the Find Controller for Geocoding in Your iOS App

By Neal Goldstein, Dave Wilson

For your geocoding functionality to work in your iOS app, you’re going to need to do several things in the Find controller. Most of what you need to do revolves around getting the text the user enters. You’ll also have to have the text geocoded and have the geocoded location implemented as an Annotation by the Trip model object, which the Find controller will add to the map.

To access the text, you first need to create an outlet for the Text field. Follow these steps:

  1. Select Main_iPad.storyboard in the Project navigator.

  2. Select the Master View controller in the Document Outline.

    Xcode's assistant editor button.

  3. Select the Assistant in the Editor selector, and if the MasterViewController.h file doesn’t appear, navigate to it using the Jump bar.

  4. In the Document Outline, open the disclosure triangle for the second Table View section in the Master View Controller – Master Scene to get to the Table View cell.

  5. Open the Table View Cell to reveal the Content View holding the Find label and text.

  6. Open that cell’s disclosure triangle to display the Text field, and then control-drag from the No Border Style Text field to the Master View controller interface (in the Assistant editor) between the @interface and –d compiler directives, and add an outlet named findText.

    Creating an outlet in an app's interface.

This is another way to create the outlet.

A UITextField object is a control that displays editable text and sends a message to its delegate when the user presses the Return key. You typically use a UITextField object to enable the user to enter small amounts of text and then do something with it — like search for something or add a new contact.

The text field icon in Xcode.

Select the Text field, select the Standard editor in the Editor selector on the Xcode toolbar, and then open the Utility area. You can set a number of Text field properties in the Attributes inspector.

Preview of an app in Xcode.

How would you know when the user has entered some text? Also, how do you get the keyboard to show, and then hide?

When a user taps in a UITextField, it becomes the first responder, and the keyboard automatically rises to allow the user to enter text — you don’t have to do a thing to make that happen.

When the user is done entering text, he taps the Return key — the Return key whose label you managed to change to Go.

When the Go key is tapped, the text field determines whether it has a delegate and whether the delegate has implemented a textFieldShouldReturn: method — one of the optional UITextFieldDelegate protocol methods. If the delegate has done so, it sends the delegate the textFieldShouldReturn: message. So textFieldShouldReturn: is the place to capture the text.

To capture the text and send it on to the FindController, you need to become the text field’s delegate and implement the textFieldShouldReturn: method. But before you do that, you need to do one more thing in Interface Builder.

You start by making the MasterViewController a UITextFieldDelegate. Update MasterViewController.h with the bolded code to have it adopt the UITextFieldDelegate protocol.

#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController
          <UITextFieldDelegate>
@property (strong, nonatomic)
    DetailViewController *detailViewController;
@property (weak, nonatomic)
  IBOutlet UITextField *findText;
–d

The heavy lifting will be done in the TextField’s textFieldShouldReturn: delegate method. The delegate will be passed the Text field being edited as an argument, and the Master View controller will pass that on to the Find controller.

First, you have to update the MasterViewController implementation by adding the bolded code to MasterViewController.m.

#import "MasterViewController.h"
#import "DetailViewController.h"
#import "AppDelegate.h"
#import "Trip.h"
#import "FindController.h"
@implementation MasterViewController

You’ll need to make MasterViewController the textField delegate. To do that, add the code in bold to viewDidLoad in MasterViewController.m.

- (void)viewDidLoad
{
 [super viewDidLoad];
 AppDelegate* appDelegate =
    [[UIApplication sharedApplication] delegate];
 self.title = appDelegate.trip.destinationName;
 UIImageView* imageView = [[UIImageView alloc]
  initWithImage:[appDelegate.trip destinationImage]];
 self.tableView.backgroundView = imageView;
 UISwipeGestureRecognizer *swipeGesture =
 [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:)];
 swipeGesture.direction = UISwipeGestureRecognizerDirectionLeft;
 [self.view addGestureRecognizer:swipeGesture];
 self.findText.delegate = self;
}

Now you can implement the textFieldShouldReturn: delegate method by adding the code to MasterViewController.m.

You’ll notice some Live Issue errors here. You’ll need to add the findLocation property to the FindController.

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
 [textField resignFirstResponder];
 if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad){
 FindController * findController =
  [[UIStoryboard
  storyboardWithName:@"Main_iPad" bundle:nil]
   instantiateViewControllerWithIdentifier:@"Find"];
 findController.findLocation = textField.text;
 DetailViewController *currentDetailViewController;
 currentDetailViewController =
 [self.splitViewController.viewControllers lastObject];
 if (
  currentDetailViewController.masterPopoverController != nil)
  [currentDetailViewController.
     masterPopoverController
        dismissPopoverAnimated:YES];
 self.splitViewController.delegate = findController;
 findController.popOverButton =
    currentDetailViewController.popOverButton;
 findController.masterPopoverController =
  currentDetailViewController.
         masterPopoverController;
 NSMutableArray* controllers =
  [NSMutableArray arrayWithObjects:
  (self.splitViewController.viewControllers)[0],
   findController, nil];
 self.splitViewController.viewControllers =
            controllers;
 }
 else {
 FindController *findController =
 [[UIStoryboard
  storyboardWithName:@"Main_iPhone"
            bundle:nil]
   instantiateViewControllerWithIdentifier:@"Find"];
 findController.findLocation = textField.text;
 [self.navigationController
   pushViewController:findController animated:YES];
 }
 return YES;
}

The first thing the code does for you is to send a message to the Text field asking it to resign as first responder:

[textField resignFirstResponder];

This has the side effect of dismissing the keyboard.

What you do next is another case where what happens depends on whether your app is running on an iPad or iPhone.

If you’re running on an iPad, you instantiate FindController from Main_iPad.storyboard.

FindController * findController =
 [[UIStoryboard storyboardWithName:
  @"Main_iPad" bundle:nil]
   instantiateViewControllerWithIdentifier:@"Find"];

You then assign the text from textField to the FindController findLocation property (which you’ll add shortly to the FindController).

findController.findLocation = textField.text;

You then dismiss the popover.

DetailViewController *currentDetailViewController;
 currentDetailViewController =
 [self.splitViewController.viewControllers lastObject];
if (currentDetailViewController.masterPopoverController
             != nil)
 [currentDetailViewController.
     masterPopoverController
        dismissPopoverAnimated:YES];

Then you assign the popOverButton and masterPopoverController properties and make FindController the Split View controller delegate.

self.splitViewController.delegate = findController;
 findController.popOverButton =
    currentDetailViewController.popOverButton;
 findController.masterPopoverController =
  currentDetailViewController.
         masterPopoverController;

Then you simply make FindController the new Detail View controller in the Split View controller’s viewControllers property.

NSMutableArray* controllers =
  [NSMutableArray arrayWithObjects:
  (self.splitViewController.viewControllers)[0],
     findController, nil];
self.splitViewController.viewControllers = controllers;

Note that, if it’s an iPhone you’re dealing with, you instantiate the FindController, assign the findLocation property, and push it on the Navigation controller stack, which causes the view to slide into place.

FindController *findController =
 [[UIStoryboardstoryboardWithName:
  @"Main_iPhone" bundle:nil]
   instantiateViewControllerWithIdentifier:@"Find"];
 findController.findLocation = textField.text;
 [self.navigationController
   pushViewController:findController animated:YES];

You finally return YES to have the Text field implement its default behavior.