Being a MapView Delegate

When Whereami launches, we want it to find the current location and display it on a map. In the last chapter, you worked directly with Core Location to find the user’s location. Now this won’t be necessary because an instance of MKMapView knows how to use Core Location to find the user’s location. All you have to do is set the showsUserLocation property of an MKMapView to YES, and it will show the user’s location on the map.

At the end of application:​didFinishLaunchingWithOptions:, replace the message that tells the locationManager to update its location with one that tells the MKMapView to show the current location.

-​ ​(​B​O​O​L​)​a​p​p​l​i​c​a​t​i​o​n​:​(​U​I​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​a​p​p​l​i​c​a​t​i​o​n​
 ​ ​ ​ ​d​i​d​F​i​n​i​s​h​L​a​u​n​c​h​i​n​g​W​i​t​h​O​p​t​i​o​n​s​:​(​N​S​D​i​c​t​i​o​n​a​r​y​ ​*​)​l​a​u​n​c​h​O​p​t​i​o​n​s​
{​
 ​ ​ ​ ​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​=​ ​[​[​C​L​L​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​s​e​t​D​e​l​e​g​a​t​e​:​s​e​l​f​]​;​

 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​s​e​t​D​i​s​t​a​n​c​e​F​i​l​t​e​r​:​k​C​L​D​i​s​t​a​n​c​e​F​i​l​t​e​r​N​o​n​e​]​;​
 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​s​e​t​D​e​s​i​r​e​d​A​c​c​u​r​a​c​y​:​k​C​L​L​o​c​a​t​i​o​n​A​c​c​u​r​a​c​y​B​e​s​t​]​;​

 ​ ​ ​ ​/​/​ ​[​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​s​t​a​r​t​U​p​d​a​t​i​n​g​L​o​c​a​t​i​o​n​]​;​
 ​ ​ ​ ​[​w​o​r​l​d​V​i​e​w​ ​s​e​t​S​h​o​w​s​U​s​e​r​L​o​c​a​t​i​o​n​:​Y​E​S​]​;​

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​l​i​n​e​ ​m​a​y​ ​s​a​y​ ​s​e​l​f​.​w​i​n​d​o​w​,​ ​d​o​n​'​t​ ​w​o​r​r​y​ ​a​b​o​u​t​ ​t​h​a​t​
 ​ ​ ​ ​[​[​s​e​l​f​ ​w​i​n​d​o​w​]​ ​m​a​k​e​K​e​y​A​n​d​V​i​s​i​b​l​e​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​

Build and run the application. A few moments after the application launches, the map will display a blue annotation dot on your current location. Unfortunately, because you are looking at a map of the entire world, the blue dot is the size of Brazil and not exactly useful for figuring out where you are. Clearly, the application needs to zoom in on the current location. Let’s figure out when and how we can do this.

For now, assume there is a zoom-in-on-location message you can send to an instance of MKMapView. The question is when would you send that message? When the application starts, it takes time for the device to determine the location. So you can’t send it in application:​didFinishLaunchingWithOptions: because you don’t yet know the location to zoom in on. Nor do you want to continually tell the MKMapView to zoom its map; that would be inefficient.

Instead, how about delegation? MKMapView has a delegate property that you set to be the instance of WhereamiAppDelegate. In WhereamiAppDelegate.h, declare that WhereamiAppDelegate conforms to the MKMapViewDelegate protocol.

@​i​n​t​e​r​f​a​c​e​ ​W​h​e​r​e​a​m​i​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​
 ​ ​ ​ ​<​U​I​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​,​ ​C​L​L​o​c​a​t​i​o​n​M​a​n​a​g​e​r​D​e​l​e​g​a​t​e​,​ ​M​K​M​a​p​V​i​e​w​D​e​l​e​g​a​t​e​>​
{​

(While you do not have to declare that a class conforms to a delegate protocol, it is helpful to do so. First, a quick glance at the header file tells you that the class serves as a delegate for a particular type of object. Second, and perhaps more important, Xcode will see this declaration and offer code completion in the implementation file for the methods in that protocol.)

The map view will send messages to its delegate when interesting events happen. Perhaps there is a message in the MKMapViewDelegate protocol for when the map view finds the user’s location. That would be the perfect time to do the zoom. We can find out if the protocol declares such a message in the Apple documentation.

Using the documentation

There’s nothing more important we can teach you than how to use the Apple documentation. So hang on as we tackle – step-by-step – the questions of when and how to display a zoomed-in map of the current location.

Overall, the documentation has four parts: API Reference, System Guides, Tools Guides, and Sample Code. The API Reference shows you every class, protocol, function, structure, method, and anything else you may use from Cocoa Touch. The System Guides give you high-level overviews and discussion about concepts in Cocoa Touch. The Tools Guide is the manual for Xcode and the rest of the developer tools suite.

While all four parts are useful, the API Reference is absolutely essential to everyday programming. There are so many classes and methods built into Cocoa Touch that it is impossible for a developer to remember them all. At no point in your iOS developer career will you outgrow the API Reference.

From the Help menu, choose Documentation and API Reference. The organizer window will appear with the Documentation item selected (Figure 5.7). Click the magnifying glass icon and choose Show Find Options to show the Find Options panel, which allows you to tailor your search. In the search box, enter MKMapViewDelegate.

Figure 5.7  Documentation Window

Documentation Window

When you search for a term, the results from each part of the documentation are listed in the table on the left side of the window. The top section titled Reference is the API Reference.

Search results from the API Reference are contained in a number of nested categories. Each result has an icon that indicates whether it is a class, a method, a protocol or something else. Collectively, we call these items symbols, and their mappings are shown in Figure 5.8.

Figure 5.8  Documentation symbol guide

Documentation symbol guide

In your search results, look under the Reference heading for an item titled MKMapViewDelegate and labeled with a Pr icon. Select that item to see the reference page for the MKMapViewDelegate protocol (Figure 5.9). Then scroll down to the Tasks section, which groups the protocol’s methods by what they used are for.

Figure 5.9  MKMapViewDelegate Protocol Reference

MKMapViewDelegate Protocol Reference

Recall that we’re looking for a method that the MKMapView will send its delegate when it has found the user’s location. See anything interesting? How about mapView:didUpdateUserLocation:? Blue text in the documentation indicates hyperlinks, so you can click on this method name to get more details (Figure 5.10).

Figure 5.10  A method in the API Reference

A method in the API Reference

The documentation confirms that this is the method we need, so go ahead and implement a stub for it in WhereamiAppDelegate.m. (If possible, copy and paste the method signature from the documentation; this is best for delegate methods where typos and capitalization errors are especially difficult to diagnose.)

-​ ​(​v​o​i​d​)​m​a​p​V​i​e​w​:​(​M​K​M​a​p​V​i​e​w​ ​*​)​m​v​ ​d​i​d​U​p​d​a​t​e​U​s​e​r​L​o​c​a​t​i​o​n​:​(​M​K​U​s​e​r​L​o​c​a​t​i​o​n​ ​*​)​u​
{​

/​/​ ​H​e​r​e​ ​w​e​ ​a​r​e​.​.​.​ ​ ​b​u​t​ ​h​o​w​ ​d​o​ ​w​e​ ​a​c​t​u​a​l​l​y​ ​z​o​o​m​?​

}​

Now we know when to zoom, and we can turn our attention to the problem of how. To problem-solve in programming, it’s best to start with the goal and what we already know. The goal is to display a map that is zoomed in on the user’s current location. We know that when the MKMapView finds the user’s location, it sends the message mapView:didUpdateUserLocation: to its delegate. We also know that, in the mapView:didUpdateUserLocation: method, a pointer to an MKUserLocation instance will be available.

In addition, we know from experience that the MKMapView does not automatically zoom in when it finds the user’s location, so it must be told to do so. This, of course, means that MKMapView must implement a method that zooms in on a location. Let’s track this method down in the API Reference.

Search for the MKMapView class. In the class reference page, look for Manipulating the Visible Portion of the Map in the Tasks section. There are a handful of methods and properties in this section; we’ll start at the top with the region property. The details for region tell us that this property is of type MKCoordinateRegion and that it provides an implicit zoom. Sounds perfect. But to set this property, we need to know more about MKCoordinateRegion.

Search for MKCoordinateRegion. Its details are in the Map Kit Data Types Reference. MKCoordinateRegion has two members of types CLLocationCoordinate2D and MKCoordinateSpan. The CLLocationCoordinate2D is the center of the map and the MKCoordinateSpan determines the level of zoom (Figure 5.11).

Figure 5.11  Parts of an MKCoordinateRegion

Parts of an MKCoordinateRegion

To set the region property of the map view, we’ll need to package up one of these instances, so let’s find out how we can do this. Search again for MKCoordinateRegion and this time select the Map Kit Functions Reference. One of these functions, MKCoordinateRegionMakeWithDistance, allows you to specify a region with a CLLocationCoordinate2D and the north-south and east-west limits of the zoom in meters. For the limits, we’ll use 250 by 250 meters. For the coordinate, we need the user’s location. Where can we get that?

How about the MKUserLocation object that the MKMapView sends its delegate in the mapView:didUpdateUserLocation: message? Search the documentation for MKUserLocation, and you’ll find it has a property called location that holds the current location of the device. Keep drilling down, and you’ll find that location is a CLLocation object, which has a coordinate property of type CLLocationCoordinate2D. Success! We can use information in the MKUserLocation to prepare an MKCoordinateRegion, which we then can use to set the region property of the map view.

Right now, getting the information from the MKUserLocation takes two steps: we send MKUserLocation the message location and then send the returned CLLocation object the message coordinate. The data returned from coordinate then becomes the MKCoordinateRegion’s center.

But nosing around the API Reference has its rewards. Before we add this code to WhereamiAppDelegate.m, take another look at the MKUserLocation reference. At the top, it tells us that MKUserLocation conforms to the protocol MKAnnotation. Click on the link for that protocol, and you’ll see that classes conforming to it are required to have a property named coordinate of type CLLocationCoordinate2D. So we can simplify the process and send the message coordinate directly to the MKUserLocation.

Now, in WhereamiAppDelegate.m, add the new code to mapView:didUpdateUserLocation:.

-​ ​(​v​o​i​d​)​m​a​p​V​i​e​w​:​(​M​K​M​a​p​V​i​e​w​ ​*​)​m​v​ ​d​i​d​U​p​d​a​t​e​U​s​e​r​L​o​c​a​t​i​o​n​:​(​M​K​U​s​e​r​L​o​c​a​t​i​o​n​ ​*​)​u​
{​
 ​ ​ ​ ​C​L​L​o​c​a​t​i​o​n​C​o​o​r​d​i​n​a​t​e​2​D​ ​l​o​c​ ​=​ ​[​u​ ​c​o​o​r​d​i​n​a​t​e​]​;​
 ​ ​ ​ ​M​K​C​o​o​r​d​i​n​a​t​e​R​e​g​i​o​n​ ​r​e​g​i​o​n​ ​=​ ​M​K​C​o​o​r​d​i​n​a​t​e​R​e​g​i​o​n​M​a​k​e​W​i​t​h​D​i​s​t​a​n​c​e​(​l​o​c​,​ ​2​5​0​,​ ​2​5​0​)​;​
 ​ ​ ​ ​[​w​o​r​l​d​V​i​e​w​ ​s​e​t​R​e​g​i​o​n​:​r​e​g​i​o​n​ ​a​n​i​m​a​t​e​d​:​Y​E​S​]​;​
}​

Notice that the MKMapView is sent the message setRegion:animated: instead of simply setRegion:. What’s the difference? Check the documentation.

Build and run the application again. When the map figures out where you are in the world, it zooms in on that location.

This is pretty standard workflow for iOS programming: you want an object to do something and you follow the bread crumbs in the API Reference. There will be dead ends and wild goose chases, but eventually you’ll find what you need. As you go through this book, don’t hesitate to look up the classes, protocols, and methods we use to see what else they can do. You want to become as comfortable as possible with the API Reference. The more you use it, the easier it will be to progress as an iOS developer. You cannot be an iOS developer without using the API Reference.

Apple will continue to update the iOS SDK and introduce iOS devices with new features and capabilities. If you understand and are comfortable using the Apple documentation, you will be ready to use whatever Apple dreams up in the future.

Your own MKAnnotation

Now that Whereami displays a nicely-zoomed map of the current location, we can turn to adding annotations. Let’s start with an introduction to the MKAnnotation protocol.

MKAnnotation is not a delegate protocol. Instead, it declares a set of methods that are useful to any class that wants to put itself on the map. Imagine an application that maps everything in a neighborhood, including restaurants, factories, and train stations. These objects could be very different and hierarchically unrelated in your application, but they all can be added to a map view if they conform to MKAnnotation.

When an object conforming to MKAnnotation is added to an MKMapView, an instance of MKAnnotationView (or one of its subclasses) is created and added to the map view. The MKAnnotationView keeps a pointer to the MKAnnotation-conforming object that it represents so it can ask it for data as needed. The relationships between these objects are shown in Figure 5.12.

Figure 5.12  MKMapView and its annotations

MKMapView and its annotations

Now you’re going to write a new class called MapPoint that will conform to MKAnnotation. When the user tags a location, an instance of MapPoint will be created and represented on the map.

From the File menu, select New and then New File.... Then choose Cocoa Touch from the iOS section, select Objective-C class, and click Next (Figure 5.13).

Figure 5.13  Creating an NSObject subclass

Creating an NSObject subclass

On the next pane, select NSObject from the superclass list and hit Next.

A sheet will drop down for you to save the files for this class. Name the class MapPoint and click Save (Figure 5.14). This creates the files MapPoint.h and MapPoint.m and adds them to your project.

Figure 5.14  Naming the subclass

Naming the subclass

In MapPoint.h, declare that MapPoint conforms to MKAnnotation. Also declare two properties and an initializer.

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​C​o​r​e​L​o​c​a​t​i​o​n​/​C​o​r​e​L​o​c​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​M​a​p​K​i​t​/​M​a​p​K​i​t​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​M​a​p​P​o​i​n​t​ ​:​ ​N​S​O​b​j​e​c​t​ ​<​M​K​A​n​n​o​t​a​t​i​o​n​>​
{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​t​i​t​l​e​;​
 ​ ​ ​ ​C​L​L​o​c​a​t​i​o​n​C​o​o​r​d​i​n​a​t​e​2​D​ ​c​o​o​r​d​i​n​a​t​e​;​
}​

/​/​ ​A​ ​n​e​w​ ​d​e​s​i​g​n​a​t​e​d​ ​i​n​i​t​i​a​l​i​z​e​r​ ​f​o​r​ ​i​n​s​t​a​n​c​e​s​ ​o​f​ ​M​a​p​P​o​i​n​t​
-​ ​(​i​d​)​i​n​i​t​W​i​t​h​C​o​o​r​d​i​n​a​t​e​:​(​C​L​L​o​c​a​t​i​o​n​C​o​o​r​d​i​n​a​t​e​2​D​)​c​ ​t​i​t​l​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​t​;​

/​/​ ​T​h​i​s​ ​i​s​ ​a​ ​r​e​q​u​i​r​e​d​ ​p​r​o​p​e​r​t​y​ ​f​r​o​m​ ​M​K​A​n​n​o​t​a​t​i​o​n​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​a​d​o​n​l​y​)​ ​C​L​L​o​c​a​t​i​o​n​C​o​o​r​d​i​n​a​t​e​2​D​ ​c​o​o​r​d​i​n​a​t​e​;​

/​/​ ​T​h​i​s​ ​i​s​ ​a​n​ ​o​p​t​i​o​n​a​l​ ​p​r​o​p​e​r​t​y​ ​f​r​o​m​ ​M​K​A​n​n​o​t​a​t​i​o​n​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​c​o​p​y​)​ ​N​S​S​t​r​i​n​g​ ​*​t​i​t​l​e​;​

@​e​n​d​

The protocol defines coordinate as a read-only property, which means there is a method named coordinate that returns a CLLocationCoordinate2D. While most methods declared in the MKAnnotation protocol are optional, the coordinate method is required – if MapPoint is to conform to the MKAnnotation protocol, it must implement coordinate.

Switch to MapPoint.m. (The keyboard shortcut for switching between the header file and the implementation file is Command-Control-Up arrow.) Synthesize the properties and add the implementations for the initializer and dealloc.

#​i​m​p​o​r​t​ ​"​M​a​p​P​o​i​n​t​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​M​a​p​P​o​i​n​t​

@​s​y​n​t​h​e​s​i​z​e​ ​c​o​o​r​d​i​n​a​t​e​,​ ​t​i​t​l​e​;​

-​ ​(​i​d​)​i​n​i​t​W​i​t​h​C​o​o​r​d​i​n​a​t​e​:​(​C​L​L​o​c​a​t​i​o​n​C​o​o​r​d​i​n​a​t​e​2​D​)​c​ ​t​i​t​l​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​t​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​o​o​r​d​i​n​a​t​e​ ​=​ ​c​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​T​i​t​l​e​:​t​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

-​ ​(​v​o​i​d​)​d​e​a​l​l​o​c​
{​
 ​ ​ ​ ​[​t​i​t​l​e​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​
@​e​n​d​

Note that you don’t release coordinate in the dealloc method because it is not an Objective-C object and can’t receive messages. The CLLocationCoordinate2D structure’s memory will live inside each instance of MapPoint, and it will be created and destroyed automatically along with the object.

The protocol defines the required coordinate as a read-only property, which means there must be a method named coordinate that returns a CLLocationCoordinate2D, but it doesn’t have to be a property in the class declaration. In fact, we don’t have to create the matching instance variables, either. The MKAnnotation protocol, like all protocols, only dictates method signatures. As long as the signatures match exactly, the conforming class can implement them however it wants with whatever instance variables it chooses. For example, the title method could perform some logic with information is has available to it and then return a value:

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​t​i​t​l​e​
{​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​ ​i​s​E​a​s​t​O​f​T​h​e​M​i​s​s​i​s​s​i​p​p​i​]​)​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​@​"​B​u​y​i​n​g​ ​s​u​p​p​l​i​e​s​"​

 ​ ​ ​ ​r​e​t​u​r​n​ ​@​"​O​n​ ​t​h​e​ ​O​r​e​g​o​n​ ​T​r​a​i​l​,​ ​u​n​c​h​a​r​t​e​d​ ​t​e​r​r​i​t​o​r​y​"​;​
}​

Therefore, we can think of a protocol as a contract, whereby the conforming class says, I promise to give you my interpretation of this contract when asked. The objects that speak to the conforming class through the protocol honor this contract by saying, I promise to only ask you things in this contract. For example, MKAnnotationView has a annotation property declared as

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​i​d​ ​<​M​K​A​n​n​o​t​a​t​i​o​n​>​ ​a​n​n​o​t​a​t​i​o​n​;​

This declaration says that the annotation can be of any type (id), as long as it conforms to the MKAnnotation protocol (<MKAnnotation>). Therefore, the MKAnnotationView will only send messages from the MKAnnotation protocol to its annotation; it won’t make any assumptions about the other messages that object might respond to.

You’ve added a lot of code, so you may want to build the application to check for syntax errors before you continue. There’s no need to run it, however, because the application’s behavior has not changed.

Tagging locations

Now that you have your own class that conforms to MKAnnotation, you can tag locations on the map. The user will enter the location’s name in the UITextField and then tap the Done button on the keyboard. The tapping of the Done button is the signal to add an annotation. How will we know this event has occurred? Delegation, of course.

In the XIB file, you set the text field’s delegate to be the instance of WhereamiAppDelegate. This means WhereamiAppDelegate can implement methods from the UITextFieldDelegate protocol. One of these methods is textFieldShouldReturn:. When the keyboard’s return key is tapped, the UITextField sends this message to its delegate and asks if it really should return. At the same time, the delegate has the opportunity to perform tasks that should coincide with the returning of the text field.

In WhereamiAppDelegate.h, declare that WhereamiAppDelegate conforms to the UITextFieldDelegate protocol.

@​i​n​t​e​r​f​a​c​e​ ​W​h​e​r​e​a​m​i​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​
 ​ ​ ​ ​<​U​I​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​,​ ​C​L​L​o​c​a​t​i​o​n​M​a​n​a​g​e​r​D​e​l​e​g​a​t​e​,​ ​M​K​M​a​p​V​i​e​w​D​e​l​e​g​a​t​e​,​
 ​ ​ ​ ​U​I​T​e​x​t​F​i​e​l​d​D​e​l​e​g​a​t​e​>​
{​

In WhereamiAppDelegate.m, implement textFieldShouldReturn:.

-​ ​(​B​O​O​L​)​t​e​x​t​F​i​e​l​d​S​h​o​u​l​d​R​e​t​u​r​n​:​(​U​I​T​e​x​t​F​i​e​l​d​ ​*​)​t​f​
{​
 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​m​e​t​h​o​d​ ​i​s​n​'​t​ ​i​m​p​l​e​m​e​n​t​e​d​ ​y​e​t​ ​-​ ​b​u​t​ ​w​i​l​l​ ​b​e​ ​s​o​o​n​.​
 ​ ​ ​ ​[​s​e​l​f​ ​f​i​n​d​L​o​c​a​t​i​o​n​]​;​

 ​ ​ ​ ​[​t​f​ ​r​e​s​i​g​n​F​i​r​s​t​R​e​s​p​o​n​d​e​r​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​Y​E​S​;​
}​

For now, ignore findLocation. You will write the implementation for that in a moment. First, let’s talk about text editing and the first responder.

UIResponder is a class in the UIKit framework. A responder is responsible for receiving and handling events that are associated with it. For example, a button is a responder that handles touch events, like a tap. In addition, one of the responders is the first responder of the window. Only one responder can be the first responder at a time. The first responder handles events that aren’t associated with another responder. For instance, a tap is sent to the responder object that was tapped, but a shake has no associated responder and is sent to the first responder instead. We’ll talk more about the first responder and event-handling in Chapter 8 and Chapter 20.

For now, let’s focus on UITextField. A UITextField is also a responder: it is a direct subclass of UIControl, which is a subclass of UIView, which is a subclass of UIResponder. When a UITextField is tapped, it handles this event by becoming the first responder.

When a UITextField becomes the first responder, a keyboard appears on the screen. To remove the keyboard from the screen, you tell the UITextField to give up its first responder status by sending it the message resignFirstResponder. Once the first responder of the window is no longer a UITextField, the keyboard will disappear.

(Everything about UITextField holds true for the class UITextView, too. The difference between UITextView and UITextField is that UITextView allows for multi-line editing: a text view’s return key enters the newline character whereas a text field’s return key dispatches the delegate method textFieldShouldReturn:.)

Putting the pieces together

To finish your Whereami application, you just need to add two final methods: findLocation, which is sent in textFieldShouldReturn:, and foundLocation:, which will be sent in locationManager:​didUpdateToLocation:​fromLocation:. In WhereamiAppDelegate.h, declare these two methods.

@​i​n​t​e​r​f​a​c​e​ ​W​h​e​r​e​a​m​i​A​p​p​D​e​l​e​g​a​t​e​ ​:​ ​N​S​O​b​j​e​c​t​
 ​ ​ ​ ​<​U​I​A​p​p​l​i​c​a​t​i​o​n​D​e​l​e​g​a​t​e​,​ ​C​L​L​o​c​a​t​i​o​n​M​a​n​a​g​e​r​D​e​l​e​g​a​t​e​,​ ​M​K​M​a​p​V​i​e​w​D​e​l​e​g​a​t​e​,​
 ​ ​ ​ ​ ​U​I​T​e​x​t​F​i​e​l​d​D​e​l​e​g​a​t​e​>​
{​
 ​ ​ ​ ​C​L​L​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​*​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​;​

 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​M​K​M​a​p​V​i​e​w​ ​*​w​o​r​l​d​V​i​e​w​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​U​I​A​c​t​i​v​i​t​y​I​n​d​i​c​a​t​o​r​V​i​e​w​ ​*​a​c​t​i​v​i​t​y​I​n​d​i​c​a​t​o​r​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​U​I​T​e​x​t​F​i​e​l​d​ ​*​l​o​c​a​t​i​o​n​T​i​t​l​e​F​i​e​l​d​;​
}​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​I​B​O​u​t​l​e​t​ ​U​I​W​i​n​d​o​w​ ​*​w​i​n​d​o​w​;​

-​ ​(​v​o​i​d​)​f​i​n​d​L​o​c​a​t​i​o​n​;​
-​ ​(​v​o​i​d​)​f​o​u​n​d​L​o​c​a​t​i​o​n​:​(​C​L​L​o​c​a​t​i​o​n​ ​*​)​l​o​c​;​

@​e​n​d​

The findLocation method will tell the locationManager to start looking for the current location. It will also update the user interface so that the user can’t re-enter text into the text field and will start the activity indicator spinning. The foundLocation: method will create an instance of MapPoint and add it to the worldView. It will also handle the map’s zoom and reset the states of the UI elements and the locationManager.

In WhereamiAppDelegate.m, import MapPoint.h and implement the two methods.

#​i​m​p​o​r​t​ ​"​W​h​e​r​e​a​m​i​A​p​p​D​e​l​e​g​a​t​e​.​h​"​
#​i​m​p​o​r​t​ ​"​M​a​p​P​o​i​n​t​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​W​h​e​r​e​a​m​i​A​p​p​D​e​l​e​g​a​t​e​

-​ ​(​v​o​i​d​)​f​i​n​d​L​o​c​a​t​i​o​n​
{​
 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​s​t​a​r​t​U​p​d​a​t​i​n​g​L​o​c​a​t​i​o​n​]​;​
 ​ ​ ​ ​[​a​c​t​i​v​i​t​y​I​n​d​i​c​a​t​o​r​ ​s​t​a​r​t​A​n​i​m​a​t​i​n​g​]​;​
 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​T​i​t​l​e​F​i​e​l​d​ ​s​e​t​H​i​d​d​e​n​:​Y​E​S​]​;​
}​

-​ ​(​v​o​i​d​)​f​o​u​n​d​L​o​c​a​t​i​o​n​:​(​C​L​L​o​c​a​t​i​o​n​ ​*​)​l​o​c​
{​
 ​ ​ ​ ​C​L​L​o​c​a​t​i​o​n​C​o​o​r​d​i​n​a​t​e​2​D​ ​c​o​o​r​d​ ​=​ ​[​l​o​c​ ​c​o​o​r​d​i​n​a​t​e​]​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​n​ ​i​n​s​t​a​n​c​e​ ​o​f​ ​M​a​p​P​o​i​n​t​ ​w​i​t​h​ ​t​h​e​ ​c​u​r​r​e​n​t​ ​d​a​t​a​
 ​ ​ ​ ​M​a​p​P​o​i​n​t​ ​*​m​p​ ​=​ ​[​[​M​a​p​P​o​i​n​t​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​C​o​o​r​d​i​n​a​t​e​:​c​o​o​r​d​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​i​t​l​e​:​[​l​o​c​a​t​i​o​n​T​i​t​l​e​F​i​e​l​d​ ​t​e​x​t​]​]​;​
 ​ ​ ​ ​/​/​ ​A​d​d​ ​i​t​ ​t​o​ ​t​h​e​ ​m​a​p​ ​v​i​e​w​
 ​ ​ ​ ​[​w​o​r​l​d​V​i​e​w​ ​a​d​d​A​n​n​o​t​a​t​i​o​n​:​m​p​]​;​

 ​ ​ ​ ​/​/​ ​M​K​M​a​p​V​i​e​w​ ​r​e​t​a​i​n​s​ ​i​t​s​ ​a​n​n​o​t​a​t​i​o​n​s​,​ ​w​e​ ​c​a​n​ ​r​e​l​e​a​s​e​
 ​ ​ ​ ​[​m​p​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​Z​o​o​m​ ​t​h​e​ ​r​e​g​i​o​n​ ​t​o​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n​
 ​ ​ ​ ​M​K​C​o​o​r​d​i​n​a​t​e​R​e​g​i​o​n​ ​r​e​g​i​o​n​ ​=​ ​M​K​C​o​o​r​d​i​n​a​t​e​R​e​g​i​o​n​M​a​k​e​W​i​t​h​D​i​s​t​a​n​c​e​(​c​o​o​r​d​,​ ​2​5​0​,​ ​2​5​0​)​;​
 ​ ​ ​ ​[​w​o​r​l​d​V​i​e​w​ ​s​e​t​R​e​g​i​o​n​:​r​e​g​i​o​n​ ​a​n​i​m​a​t​e​d​:​Y​E​S​]​;​

 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​T​i​t​l​e​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​@​"​"​]​;​
 ​ ​ ​ ​[​a​c​t​i​v​i​t​y​I​n​d​i​c​a​t​o​r​ ​s​t​o​p​A​n​i​m​a​t​i​n​g​]​;​
 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​T​i​t​l​e​F​i​e​l​d​ ​s​e​t​H​i​d​d​e​n​:​N​O​]​;​
 ​ ​ ​ ​[​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​s​t​o​p​U​p​d​a​t​i​n​g​L​o​c​a​t​i​o​n​]​;​
}​

Note that when importing files, you put quotation marks around header files you create and angled brackets around header files from frameworks. Angled brackets tell the compiler, Only look in your system libraries for this file. Quotation marks say, Look in the directory for this project first, and if you don’t find something, then look in the system libraries.

Finally, send the message foundLocation: when a new location is found by the CLLocationManager. Update the delegate method locationManager:​didUpdateToLocation:​fromLocation: in WhereamiAppDelegate.m:

-​ ​(​v​o​i​d​)​l​o​c​a​t​i​o​n​M​a​n​a​g​e​r​:​(​C​L​L​o​c​a​t​i​o​n​M​a​n​a​g​e​r​ ​*​)​m​a​n​a​g​e​r​
 ​ ​ ​ ​d​i​d​U​p​d​a​t​e​T​o​L​o​c​a​t​i​o​n​:​(​C​L​L​o​c​a​t​i​o​n​ ​*​)​n​e​w​L​o​c​a​t​i​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​r​o​m​L​o​c​a​t​i​o​n​:​(​C​L​L​o​c​a​t​i​o​n​ ​*​)​o​l​d​L​o​c​a​t​i​o​n​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​n​e​w​L​o​c​a​t​i​o​n​)​;​

 ​ ​ ​ ​/​/​ ​H​o​w​ ​m​a​n​y​ ​s​e​c​o​n​d​s​ ​a​g​o​ ​w​a​s​ ​t​h​i​s​ ​n​e​w​ ​l​o​c​a​t​i​o​n​ ​c​r​e​a​t​e​d​?​
 ​ ​ ​ ​N​S​T​i​m​e​I​n​t​e​r​v​a​l​ ​t​ ​=​ ​[​[​n​e​w​L​o​c​a​t​i​o​n​ ​t​i​m​e​s​t​a​m​p​]​ ​t​i​m​e​I​n​t​e​r​v​a​l​S​i​n​c​e​N​o​w​]​;​

 ​ ​ ​ ​/​/​ ​C​L​L​o​c​a​t​i​o​n​M​a​n​a​g​e​r​s​ ​w​i​l​l​ ​r​e​t​u​r​n​ ​t​h​e​ ​l​a​s​t​ ​f​o​u​n​d​ ​l​o​c​a​t​i​o​n​ ​o​f​ ​t​h​e​
 ​ ​ ​ ​/​/​ ​d​e​v​i​c​e​ ​f​i​r​s​t​,​ ​y​o​u​ ​d​o​n​'​t​ ​w​a​n​t​ ​t​h​a​t​ ​d​a​t​a​ ​i​n​ ​t​h​i​s​ ​c​a​s​e​.​
 ​ ​ ​ ​/​/​ ​I​f​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n​ ​w​a​s​ ​m​a​d​e​ ​m​o​r​e​ ​t​h​a​n​ ​3​ ​m​i​n​u​t​e​s​ ​a​g​o​,​ ​i​g​n​o​r​e​ ​i​t​.​
 ​ ​ ​ ​i​f​ ​(​t​ ​<​ ​-​1​8​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​i​s​ ​i​s​ ​c​a​c​h​e​d​ ​d​a​t​a​,​ ​y​o​u​ ​d​o​n​'​t​ ​w​a​n​t​ ​i​t​,​ ​k​e​e​p​ ​l​o​o​k​i​n​g​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​s​e​l​f​ ​f​o​u​n​d​L​o​c​a​t​i​o​n​:​n​e​w​L​o​c​a​t​i​o​n​]​;​
}​

Build and run the application. Enter a title into the text field, and an annotation with that title will appear on the map at your current location.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.210.102