iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) (24 page)

Read iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) Online

Authors: Aaron Hillegass,Joe Conway

Tags: #COM051370, #Big Nerd Ranch Guides, #iPhone / iPad Programming

BOOK: iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides)
10.26Mb size Format: txt, pdf, ePub
 

In
WhereamiViewController.m
, add the new code to
mapView:didUpdateUserLocation:
.

 
- (void)mapView:(MKMapView *)mapView
    didUpdateUserLocation:(MKUserLocation *)userLocation
{
    
CLLocationCoordinate2D loc = [userLocation coordinate];
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 250, 250);
    [worldView setRegion:region animated:YES];
}

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. Select a location from the debugger bar. The map will then zoom in on this 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 are comfortable using Apple’s documentation, you will be ready for 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 display instances of itself on a map view. Imagine an application that maps everything in a neighborhood, including restaurants, factories, and train stations. These classes could be very different and hierarchically unrelated in the application, but if they conform to
MKAnnotation
, they all can be added to an
MKMapView
.

 

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.13
.

 

Figure 5.13  MKMapView and its annotations

 
 

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

 

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

 

Figure 5.14  Creating an NSObject subclass

 
 

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

 

Figure 5.15  Naming the subclass

 
 

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

 
#import
#import
#import
@interface BNRMapPoint : NSObject

{
}
// A new designated initializer for instances of BNRMapPoint
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t;
// This is a required property from MKAnnotation
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
// This is an optional property from MKAnnotation
@property (nonatomic, copy) NSString *title;
@end
 

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.

 

Switch to
BNRMapPoint.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 implementation for the initializer.

 
#import "BNRMapPoint.h"
@implementation BNRMapPoint
@synthesize coordinate, title;
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
    self = [super init];
    if (self) {
        coordinate = c;
        [self setTitle:t];
    }
    return self;
}
@end
 

Per our initializer rules from
Chapter 2
, override
init
in
BNRMapPoint.m
to call the new designated initializer. (This method may already be implemented by the template, so make sure to replace its contents if that is the case.)

 
- (id)init
{
    return [self initWithCoordinate:CLLocationCoordinate2DMake(43.07, -89.32)
                              title:@"Hometown"];
}
 

In our code, we declared
coordinate
as a
readonly
property just like the protocol does. But we didn’t have to. We could have just implemented a
coordinate
method that returns a
CLLocationCoordinate2D
. 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.

 

As implemented,
BNRMapPoint
will return the values of its instance variables (
coordinate
and
title
) when sent the messages
coordinate
and
title
from the
MKAnnotation
protocol. However, those methods could return a constant value or perform some logic, as in the following implementation:

 
- (NSString *)title
{
    if ([self isEastOfTheMississippi])
        return @"Buying supplies"
    return @"On the Oregon Trail, uncharted territory";
}

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 an
annotation
property declared as

 
@property (nonatomic, retain) id annotation;
 

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 (Command-B) to check for syntax errors before you continue. There’s no need to run it, though, because the application’s behavior hasn’t changed.

 
Tagging locations

Now that you have a class that conforms to
MKAnnotation
, you can display instances of it 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, again.

 

In the XIB file, you set the text field’s
delegate
to be the instance of
WhereamiViewController
. This means you can send
WhereamiViewController
messages in 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. In this method, the delegate has the opportunity to perform tasks that should coincide with the returning of the text field.

 

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

 
@interface WhereamiViewController : UIViewController
    ,
    UITextFieldDelegate
>
{
 

In
WhereamiViewController.m
, implement
textFieldShouldReturn:
.

 
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    // This method isn't implemented yet - but will be soon.
    [self findLocation];
    [textField resignFirstResponder];
    return YES;
}
 

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.
UIView
is a subclass of
UIResponder
. Therefore,
UIView
objects can receive events. A button is a responder that handles touch events, like a tap. Shaking a device and tapping a key on the keyboard also generate events.

 

One of the responders is the
first responder
. Only one responder can be the first responder at a time. The first responder handles events that aren’t associated with a position on the screen. For instance, a tap is sent to the view object that was tapped, but shaking the device has no position on the screen, so that event is sent to the first responder instead. We’ll talk more about the first responder and event-handling in
Chapter 6
and
Chapter 19
.

 

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 automatically 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 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:
.)

 

Other books

Want & Need by CJ Laurence
Yes by RJ Lawrence
Fierce & Fabulous (Sassy Boyz) by Elizabeth Varlet
El rey ciervo by Marion Zimmer Bradley
The Final Cut by Michael Dobbs
An Affair of the Heart by David George Richards
Innocent Desires by Abie, Malie