Chapter 9. MapKit

I have been looking forward to writing this chapter on MapKit since the time I first conceived this book. This is the last chapter, and our journey together is almost over. It is fitting that we finish with a bang, and I am confident that this subject will not disappoint you.

During the course of this chapter, you will see that some of the coolest and most successful apps are based upon what we call the MapKit framework. The biggest reason I saved the best for last is that this topic requires as much foundation as possible in order to not overwhelm the student. Teaching this course to a lecture hall full of eager, and mostly novice, programmers, I have learned the hard way that when I succumbed to the students' enthusiasm and tried to teach MapKit midway through the semester, I led the entire class into a brick wall.

Even though MapKit provides us the means to write powerful and vivid apps, it also demands that we be quite aware of what methods, classes, and frameworks are. Originally, the scope of this book didn't include covering all of those concepts, but there was no way I could leave out MapKit!

So, before we begin, we need to sit back and look at a few things. MapKit, as a toolbox, is a very challenging set of utilities and devices, but I will show you some basics and teach you how to use them to successfully and creatively navigate the example in this chapter.

We will first talk about frameworks and classes. Next, we will see what MapKit can already do without your having to program anything. Then we will jump in at the deep end to see what other programmers have done using MapKit, and glean what we can from them. After honing your understanding of methods, and once you have acquired a decent grasp of frameworks, classes, and other Apple goodies contained in MapKit, we'll tackle–gently–the exercise.

In the latter half of the chapter, I will serve you an extended dessert in the "Digging My Students' MapKit Code" section. Rather than finishing with an eclectic mix of technical references, I will instead present three of my students' efforts in MapKit-related projects. I am hopeful that when you see what these representative students were able to accomplish, shortly after they passed my class, you will feel even more inspired to set your course for the next challenge.

My objective, then, is to get each of you to a place where you can say: I have programmed a basic iPad MapKit app, and I understand how to move forward with confidence into more advanced territory.

A Little about Frameworks

When Steve Jobs was fired from Apple, he formed a business called NeXT. His company produced beautiful, black, streamlined computers in the early '90s that made me drool with envy, for a few of my professors owned a NeXT computer. The most profound aspect of this outfit was not that they cranked out these black, streamlined boxes, but rather that they utilized a language called Objective-C. Jobs found that, even though it was difficult to program in this complex language, the code it produced was able to "talk to" the microprocessor elegantly. So what's this got to do with MapKit?!

What NeXT did was create frameworks of complex Objective-C code that you can look at as tools that a carpenter might have in his toolbox. When we use MapKit, we are actually bringing in to our own code a framework of map-related tools—just as a carpenter may have one set of tools for cabinetry and another set, specially made for intricate furniture. These tools will differ greatly from the kinds of tools that a roofing carpenter may use.

To this end, we will bring into Xcode two frameworks that we have not used before. It will be almost as if you had been learning techniques as a flooring and cabinetry carpenter in Chapters 18, but today we are going to the hardware store to get outfitted for our next gig: audio-video installations in walls and ceilings. Thus, before we head on to the next program, we are going to have to go buy two brand-new tools. One of our new tools, the CoreLocation framework, shows us where we are geographically. The other tool, MapKit, enables us to interact with maps in a multiplicity of ways.

As you know, the way users interact with the iPad and iPhone is completely different from anything seen before. Before the advent of these slick devices, 99% of all interactions we had with computers were based upon the mouse and keyboard. As you have been learning, though, from the examples that you've programmed, we have used unique methods and classes to jump between screens and to sense when a user is pinching, tapping, or scrolling on the screen. To this already formidable set of tools, we are now going to add CoreLocation and MapKit frameworks.

Most of the programming we have explored has been relatively transparent. In this chapter, it won't be so transparent. For example, we will have to really maintain our focus in order to keep track of how MapKit knows where we are–on a map. We'll look into how it follows our finger interactions and how it knows where we are in terms of the various screens and views associated with maps.

One of the central areas of iPad/iPhone app development is event handling. This is the part that confused most of my students when we got to this lesson, and I will do my best to keep you from wandering off into the briar patch. If I do a decent job of introducing the concepts of frameworks and classes to you, then you will not be burdened by having to think too much about event handling. You can get an idea of the scope of this topic by considering that while part of your app is keeping track of interacting with a map and with a GPS satellite, another portion of your code has to always be looking at when the user is going to direct the program to a new event.

Important Things to Know

There are three important things to know about the foundation of map-related applications in the iPad and iPhone arena. Apps rely on three important tools: MapKit, CoreLocation, and the MKAnnotationView class reference. As I have indicated, we are not going to involve ourselves with how these sophisticated tools work so much as to practice the art of deciding when to reach for which tool in your newly expanded toolbox.

Among other things, these tools allow us to display maps in our applications, to use annotations, to work with something called Geocoding (which works with longitude and latitude), and to interact with our location (via CoreLocation).

When we want to interact effortlessly with Google Maps, we will use the Apple-provided MapKit framework. When we want to get our location or do cool things using GPS-satellite technology (with Google Maps), we will use the CoreLocation framework. Finally, when we want to place pins on a map, create references, draw chevron marks, or insert an image of your dog showing where he is on a map, we will call these annotations and, thus, use MKAnnotationView.

Preinstalled MapKit Apps

So that you can take maximum advantage of the new ideas presented in this chapter, and be prepared to stretch and expand into a new level of creativity, we will first go on a little tour of existing apps, preinstalled on the iPad and iPhone. It is important that you become familiar with these so that you can more easily add bells and whistles to your own creations—on top of these ready-made "map apps," as described at Apple.com.

Find Locations

The Find Locations app (see Figure 9-1), preinstalled on iPhone 3GS and iPad, finds your location quickly and accurately via GPS, Wi-Fi, and cellular towers. Drop a pin to mark your location or share it with others via email or MMS.

Find Locations—a powerful zooming map function on the iPhone/iPad.

Figure 9.1. Find Locations—a powerful zooming map function on the iPhone/iPad.

Get Directions

Shown in Figure 9-2, the preinstalled Get Directions app lets you view a list of turn-by-turn directions or follow a highlighted map route and track your progress with GPS. You specify whether you'd like walking or driving directions, or see what time the next train or bus leaves with public transit directions.

Get Directions – use this in conjunction with, or in lieu of, the visual map (with highlighted route).

Figure 9.2. Get Directions – use this in conjunction with, or in lieu of, the visual map (with highlighted route).

See Which Way You're Facing

In the preinstalled See Which Way You're Facing app, shown in Figure 9-3, a built-in digital compass rotates maps so that they always match the direction you're facing. You can also use the compass on its own.

See which way you're facing - shows your orientation with built-in compass (on Model 3GS).

Figure 9.3. See which way you're facing - shows your orientation with built-in compass (on Model 3GS).

See Traffic

The preinstalled iPhone app See Traffic, illustrated in Figure 9-4, shows you live traffic information, indicating traffic speed along your route in easy-to-read green, red, and yellow highlights.

See Traffic—one of many possibilities when running 'Maps' on iPhone/iPad.

Figure 9.4. See Traffic—one of many possibilities when running 'Maps' on iPhone/iPad.

Search for a Location

In the Search For A Location mode, shown in Figure 9-5, you can find locations by address or by keyword. For example, search for "coffee" to see every cafe near you. When you find what you're looking for, tap the phone number to call (on the iPhone), tap the web address to open the website in Safari, or add it to Contacts for future reference.

The Search For A Location mode is a quick and powerful capability that brings specific destination information to your fingertips.

Figure 9.5. The Search For A Location mode is a quick and powerful capability that brings specific destination information to your fingertips.

Change Your View

The preinstalled Change Your View app, shown in Figure 9-6, lets you switch between map view, satellite view, hybrid view, and street view. You can double-tap or pinch to zoom in and out.

The Change Your View mode is a standard feature of 'Maps' on iPhone/iPad.

Figure 9.6. The Change Your View mode is a standard feature of 'Maps' on iPhone/iPad.

Cool and Popular MapKit Apps to Inspire You

I found that it really helped my students when, after showing them the prebuilt apps, we spent some time to review some super-cool third-party MapKit apps ... to inspire them and get their brains storming. So, imagine you are sitting with us and taking this brief tour as well. Here are nine MapKit apps that caught my eye, some of which I use regularly.

  • MapMyRide: This is a MapKit app I use all the time. I simply turn it on and start riding around on my bike. It tracks my speed, time, and mileage, as well as the elevation I ride. It then takes into account my age, gender, and body weight, and it tells me how many calories I burned. [On a good day, I can almost burn off two doughnuts!] The point is that this application calculates all these things while I'm just riding along huffing and puffing! When I get home, I can see the route on my computer. It does most of its work by using and manipulating preinstalled MapKit apps.

  • QuikMaps: This is a do-it-yourself map app that allows you to doodle on the map. It integrates with a number of places, including your website, Google Earth, or even your GPS.

  • 360 Cities—The World In Virtual Reality: This shows 360-degree panoramas of over 50 world cities and 6000 panoramas. It is the perfect technology for real estate agents, tour guides, and adventurers.

  • Cool Maps—7 Wonders of the World: This shows the seven wonders of the ancient world, and the seven wonders of the modern world, including natural wonders, underwater wonders, strange wonders, and local wonders. I am impressed with how slick the programmers have made the touch and feel of the app.

  • Blipstar: This app converts Internet business URL addresses to their corresponding brick–and-mortar stores, presented on a cool map.

  • Twitter Spy: This applets people see where the person who is tweeting them is currently located. Yep—wacky and crazy, but true.

  • Geo IP Tool: This app displays the longitude and latitude information of businesses on the Web, and then shows you the best ways to get there.

  • Map Tunneling Tool: This one is just funny and clever. Imagine where you would come out if you began digging a hole straight down—from wherever. Is the answer always China?

  • Tall Eye: This app shows you where you will go if you walk directly, in a straight line, around the earth, starting at one point and staying on a specific bearing all the way around.

MapKit_01: A View-Based Application

For your final exercise, you are going to begin with some boilerplate code that suits our basic requirements, and you will then modify it from there. I will tour you through some of the same building blocks and files that you've seen throughout this book, and I will be challenging you to see what areas of the code are pretty much the same as what you've encountered and what areas are different – given the nature of this application.

The ability to recognize patterns and to see structures just under the surface is a powerful aptitude that we all have, but we programmers cultivate ours to a heightened degree. So, be on the lookout for phrases, statements, suffixes, prefixes – grammatical hooks, as it were – that you can build on, modify, and/or revise. Play a little game: see if you can anticipate some of the moves I will have you take.

Possible Prepping for the App

We are going to consider a wide variety of components that we may want to build in to our app. Before that, though, I want to make sure we are all on the same page with terminology. We programmers need to recall some basic earth science and geography so that our code will be as effective as possible.

When we direct the computer to animate a pin dropping down, with annotations, onto a specific location, giving "longitude" and "latitude," we need to know what these terms really mean. Lines of latitude are the imaginary lines that circle the globe "horizontally," running east to west (or west to east). These invisible lines are measured in degrees, minutes, and seconds, north or south of the Equator. The Equator is the elliptical locus of points on the Earth's surface midway between the poles, which themselves are real points physically—defined by the Earth's rotation on its axis. Lines of latitude are often referred to as parallels. The North Pole is 90 degrees north latitude; the South Pole is 90 degrees south latitude.

Lines of longitude, on the other hand, which are often called meridians, are imaginary "vertical" lines (ellipses) always crossing through the North and South Poles. They are also measured in degrees, minutes, and seconds, east or west of the Prime Meridian, an arbitrary standard that runs through Greenwich, England. Unlike the Equator, which goes all the way around the world—360 degrees, the Prime Meridian (0 degrees longitude) is a semi-circle (semi-ellipse), extending from the North Pole to the South Pole; the other half of the arc is called the International Date Line, and it is defined as 180 degrees east and/or 180 degrees west longitude.

For our Chapter 9 app, the example I have used to demonstrate the "pin drop" on location is my office at the University of Colorado at Colorado Springs. You, of course, can use any location you choose. You may want to use your own address, or a well-known landmark. To do this, you must get the latitude and longitude values of that location,—most likely from Google Maps or a direct GPS reading. There are many sites on the Internet, too, where you can find these coordinates; Figure 9-7 illustrates one of them, www.batchgeocode.com.

http://www.batchgeocode.com/lookup is one of many Internet sites where one can enter an address and receive its longitudinal and latitudinal coordinates.

Figure 9.7. http://www.batchgeocode.com/lookup is one of many Internet sites where one can enter an address and receive its longitudinal and latitudinal coordinates.

Here's a thought ... let's start at the end of our process and think backwards for a minute. Go ahead and jump forward in this chapter for a sneak peek at what the app will look like—what results it will return if all goes well. In Figure 9-25, you will see a picture of a hybrid map showing a red pin that's sitting on top of a building. That's the Engineering Building at the University of Colorado at Colorado Springs, and the pin is located right above my office. The next picture has what we call an annotation, which is the text. "Dr. Rory Lewis" is the title, and "University of Colorado at Colorado Springs" is the subtitle.

Later in the tutorial, you will see that we need to be careful about what is the title and what is the subtitle. We control the color of the pin, and we decide on the style of animation—how the pin drops onto the map image.

In Figure 9-27, we zoom way out and see a super high-level view—from space. This allows us to see huge regions, but we obviously lose detail. This shows, too, the difference between the user's current location (blue dot) and a highlighted location (red pin). Interestingly, the iPad Simulator assumes the user is in Cupertino, CA—the location of Apple's headquarters.

This is a good place for me to remind you of the title of this book: iPhone and iPad Apps for Absolute Beginners. Take a deep breath! Even if I were meeting your greatest expectations of teaching you the most you've ever been taught, and even if you were meeting your greatest expectations of yourself—learning so much complexity in such a short time, you would still not become an expert in this challenging area of MapKit code! My humble goal here is not fluency, but reasonable familiarity and a sense of what lies ahead.

If that sounds right, let's get on with it.

Preliminaries

As in previous chapters, please download and extract images and code for this chapter. Navigate to http://www.rorylewis.com/xCode/011_MapKit_01.zip and download its contents. Then, extract the files onto your beautifully clean desktop.

The zip file contains several folders of content: MapKit_01.xcodeproj, Classes, and build, and some individual files: MapKit_01ViewController.xib, MapKit_01-Info.plist, MainWindow.xib, MapKit_01_Prefix.pch, and main.m.

Once you have extracted all the files, remember to delete the 011_MapKit_01.zip and MapKit_01 folders. We don't want any of your files to be overwritten and conflict with your exercise code.

To view the screencast of this chapter's exercise, go to http://www.rorylewis.com/docs/02_iPad_iPhone/06_iphone_Movies/011_MapKit.htm.

A New View-Based Template

  1. Open Xcode and enter

    A New View-Based Template
  2. The first thing we need to do is add two frameworks: CoreLocation and MapKit. Right-click on the Frameworks folder, click Add and then Existing Frameworks, and then, in the drop-down menu that appears, select both CoreLocation and MapKit Framework, as shown in Figure 9-9.

Select the View-based Application icon, and save your new file to your desktop.

Figure 9.8. Select the View-based Application icon, and save your new file to your desktop.

Select the CoreLocation and MapKit frameworks.

Figure 9.9. Select the CoreLocation and MapKit frameworks.

Adding the Annotation File

  1. The next thing we need is a means to control our annotation. For that, we will create a UIViewController subclass that will control all the characteristics we want to display on this annotation. Click the Classes folder and enter

    Adding the Annotation File

Make sure that your options are not checked. These include Targeted for iPad, UITableViewController Subclass, and XIB Interface. This will automatically create a myPos header file and implementation file in your Classes folder once you hit the Next button or Return.

Create a new UIViewController subclass, and name it "myPos."

Figure 9.10. Create a new UIViewController subclass, and name it "myPos."

It's Already Working!

Believe it or not, we already have enough to show a map working in our iPad or iPhone. As I mentioned in the introduction to this chapter, Apple programmers have included an enormous amount of complex Objective-C code in the two frameworks that we imported.

  1. To examine the details, let's open up your nib file by going into the Resources folder and opening up the MapKit_01ViewController.xib file. Click on it as shown in Figure 9-11.

Open up the MapKit_01ViewController.xib file to see how the frameworks can already implement a working map.

Figure 9.11. Open up the MapKit_01ViewController.xib file to see how the frameworks can already implement a working map.

Check It Out—the iPad Simulator

  1. After opening up Interface Builder, drag a Map View object from the Library and drop it onto the View. Then go to your Inspector and click Show User Location. With this done, click

    Check It Out—the iPad Simulator

Note

Because you are not operating on an actual iPad or iPhone, the simulator pretends that your Geocode Location is that of Apple's headquarters in Cupertino, CA, the heart of Silicon Valley. Once your app—at this stage of development—is put on a real iPad or iPhone, the GPS system in your device will use the CoreLocation framework you imported in Step 2 to get your actual location.

Your screen might look slightly different initially because, by default, it will show the iPhone screen embedded in your iPad simulator. To make the screen in your iPad look exactly like my screen in Figure 9-12, simply click the Enlarge button at the bottom-right corner of the inset screen of your iPad.

You already have a working map appearing in the iPad simulator.

Figure 9.12. You already have a working map appearing in the iPad simulator.

Make It Look a Little Bit Better

Even though we have a map appearing in our iPad that shows that our CoreLocation framework is working, we want to make it look a lot better than this. Specifically, we would like it to show the location of a point of our choosing, and we're going to want to have a pin drop down too, and include an annotation in which a title and subtitle explain something about our location. This information will appear in a little black box when the user clicks on the pinhead, of whatever color we choose.

  1. We will start by declaring our classes, methods, and outlets in our ViewController.h file and then implementing them in our ViewController.m file. At that point, we will make some adjustments to the myPos and AppDelegate files.

  2. Open up your header file by clicking on MapKit_01ViewController.h as shown in Figure 9-13. The first thing we need to do is tell our app that we have imported the MapKit framework; we do this by inserting #import <MapKit/MapKit.h> right under the line #import <UIKit/UIKit.h>. The next thing we will do is tell the header file that we will be using the MKMapViewDelegate protocol. This protocol defines a set of optional methods that our app will use to receive map update records.

  3. Now we also need to add an outlet with a pointer to the MKMapView class. We do that by typing in IBOutlet MKMapView *mapView, which declares an object of type MKMapView. The last thing we need to do is define the @property, by entering

@property (nonatomic, retain) IBOutlet MKMapView *mapView;

@end

Your code, at this point, should look as follows:

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>

@class MyPos;

@interface MapKit_01ViewController : UIViewController <MKMapViewDelegate>
{
        IBOutlet MKMapView *mapView;
}

@property (nonatomic, retain) IBOutlet MKMapView *mapView;

@end
Coding our ViewController header file.

Figure 9.13. Coding our ViewController header file.

Dealing with the Implementation

As mentioned in the introduction to this chapter, controlling and working with the MapKit and CoreLocation frameworks is not a trivial matter. Daunting as these areas can be, I could not leave them out of this book. We proceed on the basis that you have learned by now to look for familiar patterns, integrate what you can, and just follow directions when things get a bit dicey!

Note

What we are going to do in our implementation file is synthesize our property and release it in the dealloc method.

  1. As shown in Figure 9-14, we now copy the dealloc statement from the bottom of the implementation file, MapKit_01ViewController.m. We are going to paste it at the top of the file, immediately below the synthesize statement you just wrote. Then add a release for the mapView property, as shown in Figure 9-15.

The reason I asked you to put it right at the top is that I now want you to delete everything below it, down to the viewDidLoad method. See Figure 9-16.

Copy the dealloc statement from the bottom of the implementation file.

Figure 9.14. Copy the dealloc statement from the bottom of the implementation file.

Paste it at the top of the file, just below the synthesize mapView statement. Also, add a release for the mapView property.

Figure 9.15. Paste it at the top of the file, just below the synthesize mapView statement. Also, add a release for the mapView property.

The next step is deleting boilerplate implementation code all the way down to the (void)viewDidLoad { line.

Figure 9.16. The next step is deleting boilerplate implementation code all the way down to the (void)viewDidLoad { line.

Let's think about this ... We need to add the viewDidLoad method, within which we will set the map type and enable zooming, scrolling, and so on. In our case, we will set the map type to a Hybrid map. If you prefer, though, you may choose to use a Satellite map or a Street map.

At this point, we will bring in the location I requested of you in the prep section—some place of interest or personal significance. If you don't have a preference, you can go ahead and use mine, as shown in the code that follows.

  1. The first thing we do is set all the coordinate regions to zeros. Then we enter coordinates of our place of interest—which, for me, is my office at the University of Colorado at Colorado Springs. I enter Region.center.latitude = 38.893432; (the positive value denotes north of the Equator) and region.center.longitude = −104.800161; (the negative sign denotes west of the Prime Meridian). Related to these parameters, we need to set the latitude and longitude Delta = 0.01f. If your math or physics is rusty, recall that "delta" refers to the change, or difference, between two values. Finally, for the viewDidLoad, I have chosen to animate the pin when it drops by using the code [mapView setRegion:region animated:YES]. Refer to Figure 9-17.

  2. The next action is to set the view controller class as the delegate, the role that will handle the interaction between the frameworks of our mapView. We do this with [mapView setDelegate:self].

    Regarding the dropped pin and the attached label, we need to make the Annotation Object the holder of the information of our coordinates. Our Annotation View is the type of view associated with the Annotation Object. Our Annotation Object needs to comply with all the rules we will set forth in our MKAnnotation Protocol. In order to create this Annotation Object, we must define a new class, which we did when we created the myPos classes.

  3. We now need to instantiate this myPos object and add it to our map. To do this, we add the delegate function that will display the annotations on to our map. We start by having myPos name a pointer we'll call "ann." Next, we set the title, and in my case I chose to use my name. So, we get ann.title = @"Dr. Rory Lewis". We handle the subtitle similarly: ann.subtitle = @"University of Colorado at Colorado Springs". We also want the pin to drop in the center of the map: ann.coordinate = region.center. Reference all of the above with [mapView addAnnotation:ann].

At this point, we will take advantage of a boilerplate method of code that most MapKit maps use.

Note

We seldom change these chunks of code, and by the time you read this book, this next set of code may be part of a new function or a new class. The reason is that when people start using the same piece of code over and over, referring to it as "boilerplate code," that's about the time Apple decides to make a new class or function out of it, and sets it to a specific name.

For now, I have included this boilerplate code in the Downloads folder for this app. This code does two things:

  • It creates a delegate method that manages our annotation during zooming and scrolling. In other words, it keeps track of where we are—even when the user scrolls, zooms in, or zooms out of our map.

  • It creates a static identifier, which controls our "queue meaning." If it can't dequeue our annotation, it will allocate one that we choose. I have also included code that changes the pin color to red. Also, I have allowed callout views.

Make sure your - (BOOL)shouldAutorotateToInterfaceOrientation is activated along with your - (void)didReceiveMemoryWarning. Your code should now look like this:

#import "MapKit_01ViewController.h"
#import "MyPos.h"

@implementation MapKit_01ViewController

@synthesize mapView;

- (void)dealloc
{
        [mapView release];
    [super dealloc];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

        [mapView setMapType:MKMapTypeStandard];
        [mapView setZoomEnabled:YES];
        [mapView setScrollEnabled:YES];

        MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };
        region.center.latitude = 38.893432;
        region.center.longitude = −104.800161;
        region.span.longitudeDelta = 0.01f;
        region.span.latitudeDelta = 0.01f;
        [mapView setRegion:region animated:YES];

        [mapView setDelegate:self];

        MyPos *ann = [[MyPos alloc] init];
        ann.title = @"Dr. Rory Lewis";
        ann.subtitle = @"University of Colorado at Colorado Springs";
        ann.coordinate = region.center;
        [mapView addAnnotation:ann];

}

- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:
The next step is deleting boilerplate implementation code all the way down to the (void)viewDidLoad { line.
(id <MKAnnotation>)annotation { MKPinAnnotationView *pinView = nil; if(annotation != mapView.userLocation) {
static NSString *defaultPinID = @"com.invasivecode.pin";
                pinView = (MKPinAnnotationView *)[mapView
The next step is deleting boilerplate implementation code all the way down to the (void)viewDidLoad { line.
dequeueReusableAnnotationViewWithIdentifier:defaultPinID]; if (pinView == nil) pinView = [[[MKPinAnnotationView alloc]
The next step is deleting boilerplate implementation code all the way down to the (void)viewDidLoad { line.
initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease]; pinView.pinColor = MKPinAnnotationColorRed; pinView.canShowCallout = YES; pinView.animatesDrop = YES; } else { [mapView.userLocation setTitle:@"I am here"]; } return pinView; } - (BOOL)shouldAutorotateToInterfaceOrientation:
The next step is deleting boilerplate implementation code all the way down to the (void)viewDidLoad { line.
(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation == UIInterfaceOrientationPortrait); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (void)viewDidUnload { } @end
Adding coordinates, parameters, and our region setting.

Figure 9.17. Adding coordinates, parameters, and our region setting.

Coding the myPos.h File

  1. The first thing we do after opening the myPos.h file is replace the import line #import <UIKit/UIKit.h> with #import <Foundation/Foundation.h>. We do this because the foundation framework pulls in all of our foundation framework classes. The next thing we do is to add MapKit by entering #import <MapKit/MKAnnotation.h>.

  2. We set our CLLocation Class Reference to incorporate the geographical coordinates and altitude of our device, as seen in Figure 9-18. We do that with this line:

    CLLocationCoordinate2D coordinate; NSString *title; NSString *subtitle
  3. Finally, we create "@property" statements for the location, title, and subtitle, as shown in the code that follows. Once you have made these additions, save your work. See Figure 9-19.

#import <Foundation/Foundation.h>
#import <MapKit/MKAnnotation.h>

@interface MyPos : NSObject <MKAnnotation>
{
        CLLocationCoordinate2D coordinate;
        NSString *title;
        NSString *subtitle;
}

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

@end
Incorporate the geographical coordinates and altitude of our device.

Figure 9.18. Incorporate the geographical coordinates and altitude of our device.

Your "myPos" header file should look like this before you save it.

Figure 9.19. Your "myPos" header file should look like this before you save it.

The myPos.m File

  1. Here we simply synthesize our coordinate, title, and subtitle with an @synthesize statement that includes coordinate, title, and subtitle. Then, release the title and subtitle in our dealloc (deallocation) statement. Once done, your file should look similar to Figure 9-20. Save your work on this file.

This is how your myPos implementation file looks before you save it.

Figure 9.20. This is how your myPos implementation file looks before you save it.

The AppDelegate Files

As you can see by studying Figure 9-21, we don't have to do anything with the AppDelegate header file. Everything we need is already there. We will, however, add a little bit to the AppDelegate implementation file.

  1. Open MapKit_01AppDelegate.m. First, we need to override the point for customization after our app launches. We do this with [windowaddSubview:viewController.view] and [window makeKeyAndVisible]. Make sure that your dealloc statement deallocates both the viewController and the window.

Note

For some reason, when I set my app to default to the iPad platform, it did not dealloc as expected. I am operating on beta version, so this might be a minor error. Nevertheless, it is always a great idea to check on this parameter. See Figure 9-22.

  1. Save your work, and then go to the Resources folder.

The AppDelegate header file is perfect. Don't change anything!

Figure 9.21. The AppDelegate header file is perfect. Don't change anything!

The AppDelegate implementation file. Get in the habit of checking your dealloc statements.

Figure 9.22. The AppDelegate implementation file. Get in the habit of checking your dealloc statements.

Connect MapView with MKMapView

  1. As shown in Figure 9-23, go to the Resources folder to open up your nib file. In Interface Builder, connect the mapView of your File's Owner to your MKMapView, as shown in Figure 9-24.

  2. Then, go back to Xcode and compile. The iPad simulation will appear, and a pin will drop down onto the top of my office—or the different location you chose. The pin will be red, and if you click on the pinhead, it will display the annotation. As shown in Figures 9-25 through 9-27, you can go back and forth between the iPad view and the iPhone view.

In the Resources folder, click on the MapKit_01ViewController.xib file so you can make the connections and get this app running!

Figure 9.23. In the Resources folder, click on the MapKit_01ViewController.xib file so you can make the connections and get this app running!

Connect the mapView of your File's Owner to your MKMapView.

Figure 9.24. Connect the mapView of your File's Owner to your MKMapView.

Congratulations! Once again, you have successfully implemented an app of fair complexity, regardless of the fact that you started with a body of code that you modified. As you compare your own Simulator to the images ahead, bask in the glow of accomplishment.

Then, perhaps after a brief rest, I hope you will venture forward to see if some student examples in the "Digging My Students' MapKit Code" section whet your appetite for further development and challenge.

The pin immediately comes down onto our set coordinates.

Figure 9.25. The pin immediately comes down onto our set coordinates.

Clicking on the Pin, our annotation opens up. Here we have the view of the iPad Simulator set to the embedded iPhone view.

Figure 9.26. Clicking on the Pin, our annotation opens up. Here we have the view of the iPad Simulator set to the embedded iPhone view.

Zooming out we see how far away we are from our "current location," which, on the iPad simulator, defaults to Apple's headquarters in Cupertino, CA.

Figure 9.27. Zooming out we see how far away we are from our "current location," which, on the iPad simulator, defaults to Apple's headquarters in Cupertino, CA.

Digging My Students' MapKit Code

When people come up to me and say, "Hey, Dr. Lewis, I have this really great idea for a new app ...," it is amazing how often it involves using the MapKit framework. We have seen how fun and sexy this stuff is, and now you have likely gathered that delving into the code can turn into quite a complexity.

As a final buffet of tasty, high-calorie, high-tech fun and flash, I am going to share some final project scenarios with you. I certainly hope you actively follow along here, but I also want to honor the fact that you're done. You already succeeded in making it to the end of Chapter 9. So remember, this section here is like one of those "bonus feature" DVDs that Hollywood loves to include—at no extra cost. Relax and enjoy!

Parsing to MapKit from the Internet

A little background: I presented my class the MapKit session very much as I laid out the first example of this chapter. Then we moved into one of the coolest things there is with MapKit—the ability to parse, or read live info from, the ether. This feature allows users to "see" the info on their map. I'll explain this to a degree before I present three student final projects.

One of the most intriguing things we can do with MapKit is get real live information from the Internet and configure it in a way that makes the Google Map on the user's iPhone come alive with live information (weather, traffic, geographical phenomena, taxis, planes, and so on). For example, one of the most popular apps for the San Francisco Bay Area is a program demonstrated in iPhone Cool Projects (Wolfgang Ante, et al., Apress, 2009) (see Figure 9-28) called "Routesy Bay Area San Francisco Muni and BART," written by Steven Peterson.

Peterson parses all the data from the BART (Bay Area Rapid Transit, http://www.bart.gov/) web server that keeps track of how close to schedule all its trains are, where they are, and their speeds. The app parses all this data and makes it useful and relevant to users at their specific locations in the San Francisco Bay Area. In Figure 9-29, you will see their app's red icon, and then several iPhone images. The left one shows all the places a user can catch buses and trains. The middle picture uses the same code we used in our example with core location to show a user's current location with a blue icon, and where a requested station is. The right image reports to the user the relevant information on the best train given the context, the timing, and so on. The app provides data for the next three trains that will be arriving at the train station nearest the user.

In essence, the MapKit code on the iPhone is, among other things, a parsing utility. It retrieves live information from a server that most people don't even know exists, and it puts a stream of data to a novel and useful purpose.

Because of the immediate and practical results that users of Peterson's app, and others like it, can reap, I figured this would be a perfect theme to round out this book. I'll first go over some of my "Parsing with MapKits" lecture notes, and then I will show you several solid final projects created by my students on that basis.

With my students' blessing, the code for their projects can be downloaded from my website, as shown below. This gives you the opportunity to have the code on your Mac while I point out how you can modify it, learn the key features from it, or just put it on your iPad and show the guys at the bar these cool apps. Enjoy!

Apress's iPhone Cool Projects.

Figure 9.28. Apress's iPhone Cool Projects.

App icon and examples of three action screens—parsing app: "Routesy Bay Area San Francisco Muni and BART."

Figure 9.29. App icon and examples of three action screens—parsing app: "Routesy Bay Area San Francisco Muni and BART."

The code for these three student Final Projects is located as follows:

  • Stephen A. Moraco (Son): http://www.rorylewis.com/xCode/011b_TrafficCam.zip

  • Stephen M. Moraco (Father): http://www.rorylewis.com/xCode/011a_APRSkit.zip

  • Satish Rege: http://www.rorylewis.com/xCode/011c_MyTraffic.zip

MapKit Parsing

Remember that this is digging deep into the code at a level that is outside the scope of the book. However, all the instructions that follow can be seen in my students' code, which you are welcome to download. For now, just read along and see if you can follow their pattern of parsing, creating delegate objects, and so forth.

Before we look at their actual apps, consider a hypothetical scenario: Imagine that there is a Grateful Dead Server that broadcasts an update on every Deadhead's geographical location—at least those who allow themselves to be visible on the grid. This hypothetical app allows a (serious) fan of the Grateful Dead to locate all the other Deadheads nearby at any time. These fans can meet and share bootlegs, hang out, and generally relate on a plane that other Grateful Dead disciples can appreciate.

Starting Point: If we were to create such an app, just as in the "Routesy" example, we would allow users to see where they are by bringing up the Attributes Inspector and turning on a Shows User Location switch. We would create a controller called DeadHeadsView that creates an instance of a parser we'll call Gratefuldead. Then, we would make it set itself as the delegate so it receives the feedback and calls a getGratefuldead data method.

Getting Data from Web: As our parser sifts through the XML on the Grateful Dead Server, we would want it to grab Gratefuldead element data and create an instance of each Gratefuldead object. So, for each instance it creates, it calls back to us with an addGratefuldead method. We would need to implement our Gratefuldead and Parser methods on our deadHeadsViewcontroller. We might find that it's easier to think of our GratefuldeadParser.h this way:

+ (id)GratefuldeadParser; // this creates it
− (void)getGratefuldeadData; // this activates it

Add Methods to View Controller: Before adding implementation methods on our DeadHeadsView controller, we would need to implement the protocol with GratefuldeadParser Delegate and import its header file #import <GratefuldeadParser.h>. At this point, we'd be finished with the header, and we'd move to the implementation file.

First, we'd copy the two implementation methods from GratefuldeadParser.h and paste these two methods after the @synthesize statement:

@implementation DeadHeadsViewC0ntroller

@synthesize deadView

- (void)getGratefuldeadData:(Gratefuldead *)Gratefuldead;
-(void)parserFinished

Test the Parser Feed: To test the Grateful Dead Server, we would see if we could log some messages. Let's separate the two methods, delete the semicolons, add brackets, and then enter "log" as shown:

- (void)getGratefuldeadData:(Gratefuldead *)Gratefuldead {
NSLog(@"Hippie Message");
}
-(void)parserFinished{
NSLog(@"located a Dead Head at %@", Gratefuldead.place");
}

Start the Parser Method: Having implemented our delegate methods, we would need to do three things:

  1. Code the parser method. Put it into a method we could call (void)viewWillAppear. This would get called on by a view controller when its view is about to be displayed. If we were to do it this way, note that we would always want to call in our - (void)viewWillAppear method.

  2. Create an instance of our parser that we would call GratefuldeadParser. With this, we'd get GratefuldeadParser *parser = [GratefuldeadParser gratefuldeadParser]. We want to make ourselves the delegate, which means that, now, GratefuldeadParser parser.delegate = self.

  3. Two actions in this step: first, tell the parser to get the Gratefuldead data:

    [parser getGratefuldeadData];

    Second, handle its import:

    #import "GratefuldeadParser.h"

Then, when the - (void)viewWillAppear is invoked, it would create an instance of GratefuldeadParser. As it receives the locations of all the Deadheads, it shows us where they are!

Do you recall how we made sure that the user of the app would appear on the map as a blue dot? I want you to think of the blue dot as just an annotation view. When it is added to the deadView, it essentially asks its delegate for the location of itself.

Note

If we return anything other than nil, then our annotation view, instead of the blue one, will be used and then return that view.

So, looking at this, we return nil when the annotation does not equal the user's current location.

- (MKAnnotationView *)deadView:(MKDeadView *)deadView
                  viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *view = nil;
return view;

But here's the thing; we do not want to return nil for our Gratefuldead locations. Conversely, we want to do cool things when our annotation is not equal to the deadView userLocation property, which itself is an annotation:

if(annotation != deadView.userLocation) {

                // THIS IS WHERE WE DO OUR COOL STUFF
               // BECAUSE IT'S A DEADHEAD, NOT THE USER
 }

At this point we use the dequeueReusableAnnotationViewWithIdentifier, delegate method which is available for reuse the instant they are off screen. It has a handy way of storing your annotations in a separate data structure and then automatically adding and removing them from your map as the user's events require it. Note that dequeueReusableAnnotationViewWithIdentifier is about getting the reusable annotation view from the map, and it has nothing to do with adding or removing annotations:

GratefuldeadAnnotation *eqAnn = (GratefuldeadAnnotation*)annotation;
view = [self.deadView dequeueReusableAnnotationViewWithIdentifier:@"GratefuldeadLoc"];
       if(nil == view) {
        view = [[[MKPinAnnotationView alloc] initWithAnnotation:eqAnn
                                              reuseIdentifier:@"GratefuldeadLoc"]
 autorelease];
}

The annotation view goes and looks in its reuse queue to see if there are any views that can be reused if (nil == view) { ... If there are none, it returns nil - which means we need to create a new one view = [[[MKPinAnnotationView alloc] initWithAnnotation:eqAnn.

There are many creative ways to make your annotations appear and be animated with chevrons, bells and whistles, Grateful Dead beads, and so on. You can check out what's out there to make the iCandy portion of your annotations, just as you wish.

In this regard, at this point of writing your code the most important step is to review your code for errors using your NSLog debugger; this will determine whether it connects to a server of your choice. Once that is done, it becomes an issue of parsing the XML. Then, the last step is to shop for iCandy for the annotations.

Three MapKit Final Projects: CS-201 iPhone Apps, Objective-C

Following are three apps that draw heavily on parsing information from the Internet. The first two come from a father and son, both named Stephen Moraco, which completely confused me in the lecture hall, and the third is from Satish Rege. They were kind enough to write unedited bios as to why they took the class, and they included exact lecture notes and apps shown in this book, and what they got out of the course.

Biographical Info—Examples 1 & 2

Stephen A. Moraco (Son)

Stephen M. Moraco (Father)

Steve A. (Figure 9-30) is in his senior year in high school. He has been concurrently enrolled at UCCS and has taken courses for dual credit (both high school and college). I, Stephen M. (Figure 9-31), am a professional software engineer working for Agilent Technologies, Inc. Both of us have iPhones and have an interest in learning to write applications for the iPhone. The UCCS course caught our attention as a way we could learn this together. In fact, we really enjoyed Dr. Lewis' CS201 classes, in which we toured the iPhone SDK and practiced writing a number of applications. The discussions in class and then between the two of us as we were driving home always had us excited about things we could do with the iPhone. Our final projects came from these discussions. Dr. Lewis, thank you for offering this course. It provided, in our case, a wonderful time of shared learning. We couldn't have had a more enjoyable time.

Stephen A. Moraco (Son)

Figure 9.30. Stephen A. Moraco (Son)

Stephen M. Moraco (Father)

Figure 9.31. Stephen M. Moraco (Father)

Final Project—Example 1

Stephen M. Moraco's app is one that is close to his heart. Being an amateur radio hobbyist, he decided to parse Bob Bruning's WB4APR site, where Bob had developed an Automatic Position Reporting System (APRS). Very much like the example I gave in class, locating Deadheads, Stephen, the father, made an app that can locate all the Amateur Radio Operators that are within a user-specified distance from where they are at the time. I will not go over all of Stephen's code because you can download it and go over it carefully. The portions I think you should take note of are as follows: His APRSmapViewController header file sets out the road map with 3 IBOutlets, 1 IBAction, and a ViewController:

@property (nonatomic, retain) IBOutlet MKMapView *mapView;
@property (nonatomic, retain) APRSwebViewController *webBrowserController;
@property (nonatomic, retain) IBOutlet UISegmentedControl *ctlMapTypeChooser;
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView *aiActivityInd;

-(IBAction)selectMapMode:(id)sender;

In the APRSkit_MoracoDadAppDelegate implementation file, he uses the following code to give the user a chance to log in, the results of which are shown in Figure 9-32. The particulars of this step are seen in the - (void)applicationDidFinishLaunching method, which also houses the distance (radius) from the user that the system will search for matches:

- (void)applicationDidFinishLaunching:(UIApplication *)application {

     NSLog(@"MapAPRS_MoracoDadAppDelegate:applicationDidFinishLaunching - ENTRY");
    // Override point for customization after app launch

     [window addSubview:[navigationController view]];
           [window makeKeyAndVisible];

     // preload our applcation defaults
     NSUserDefaults *upSettings = [NSUserDefaults standardUserDefaults];
     NSString *strDefaultCallsign = [upSettings stringForKey:kCallSignKey];
     if(strDefaultCallsign == nil)
     {
          strDefaultCallsign = strEmptyString;
     }
     self.callSign = strDefaultCallsign;
     //[strDefaultCallsign release];

     NSString *strDefaultSitePassword = [upSettings stringForKey:kSitePasswordKey];
     if(strDefaultSitePassword == nil)
     {
          strDefaultSitePassword = strEmptyString;
     }
     self.sitePassword = strDefaultSitePassword;

     NSString *strDefaultDistanceInMiles = [upSettings
stringForKey:kDistanceInMilesKey];
     if(strDefaultDistanceInMiles == nil)
     {
          strDefaultDistanceInMiles = @"30";
     }
     self.distanceInMiles = strDefaultDistanceInMiles;
     //[strDefaultSitePassword release];
     // INCORRECT DECR [upSettings release];
}
CS-201 Final Project—Stephen M. Moraco's APRS set-up screen where users enter their Amateur Radio call signs and passwords.

Figure 9.32. CS-201 Final Project—Stephen M. Moraco's APRS set-up screen where users enter their Amateur Radio call signs and passwords.

One of the first things Stephen did when he went to the website was make a list of all the attributes in the XML feed. The following list shows what he saw.

Column-1 was the call sign, CALLSIGN

Column-2 was the URL to Message traffic if available

Column-3 was the URL to Weather page if available

Column-4 was the Latitude

Column-5 was the Longitude

Column-6 was the distance from me (in miles)

Column-7 was the DD:HH:MM:SS of last report

To account for this data, he made eight pointers in his APRSstationParser.m file. Note that he has an extra one for possible unknown columns.

NSString *kCallSignCol          = @"Callsign";
NSString *kMsgURLCol            = @"MsgURL";
NSString *kWxURLCol             = @"WxURL";
NSString *kLatitudeCol          = @"Lat";
NSString *kLongitudeCol         = @"Long";
NSString *kDistanceCol          = @"Distance";
NSString *kLastReportCol        = @"LastReport";
NSString *kUnknownCol           = @"???";// re didn't recognize column #
Then, in the same file, he made case statements:
    case 1:
               m_strColumnName = kCallSignCol;
               break;
          case 2:
               m_strColumnName = kMsgURLCol;
               break;
          case 3:
               m_strColumnName = kWxURLCol;
               break;
          case 4:
               m_strColumnName = kLatitudeCol;
               break;
          case 5:
               m_strColumnName = kLongitudeCol;
               break;
          case 6:
               m_strColumnName = kDistanceCol;
               break;
          case 7:
               m_strColumnName = kLastReportCol;
               break;
          default:
               m_strColumnName = kUnknownCol;
               break;

Also, in the APRSkit_MoracoDadAppDelegate implementation file, the - (void)recenterMap method scans all annotations to determine geographical center and, just as we did in this chapter's exercise, to calculate the region of map to display. Stephen does likewise after his three if statements. An image of the pins dropping is shown in Figure 9-33.

- (void)recenterMap {
     NSLog(@" - APRSpinViewController:recenterMap - ENTRY");
          NSArray *coordinates = [self.mapView
valueForKeyPath:@"annotations.coordinate"];
     CLLocationCoordinate2D maxCoord = {-90.0f, −180.0f};
     CLLocationCoordinate2D minCoord = {90.0f, 180.0f};
     for(NSValue *value in coordinates) {
          CLLocationCoordinate2D coord = {0.0f, 0.0f};
          [value getValue:&coord];
               if(coord.longitude > maxCoord.longitude) {
                    maxCoord.longitude = coord.longitude;
               }
               if(coord.latitude > maxCoord.latitude) {
                    maxCoord.latitude = coord.latitude;
               }
               if(coord.longitude < minCoord.longitude) {
                    minCoord.longitude = coord.longitude;
               }
               if(coord.latitude < minCoord.latitude) {
                    minCoord.latitude = coord.latitude;
               }
     }

It should be noted that in the APRSstation class, Stephen represents the details parsed from the APRS, which sets the location of the pins.

#import <CoreLocation/CoreLocation.h>

@interface APRSstation : NSObject {
     NSString   *m_strCallsign;
     NSDate     *m_dtLastReport;
     NSNumber   *m_nDistanceInMiles;
     NSString   *m_strMsgURL;
     NSString   *m_strWxURL;
     NSString   *m_strTimeSinceLastReport;
     CLLocation *m_locPosition;
     int         m_nInstanceNbr;
}

@property(nonatomic, copy) NSString *callSign;
@property(nonatomic, copy) NSNumber *distanceInMiles;
@property(nonatomic, retain) NSDate *lastReport;
@property(nonatomic, copy) NSString *timeSinceLastReport;
@property(nonatomic, copy) NSString *msgURL;
@property(nonatomic, copy) NSString *wxURL;
@property(nonatomic, retain) CLLocation *position;

@end
CS-201 Final Project—Stephen M. Moraco's Animated pins drop down within the specified radius of the user's location. Here on the iPad simulator, the pins drop in the surrounding areas of Apple Headquarters.

Figure 9.33. CS-201 Final Project—Stephen M. Moraco's Animated pins drop down within the specified radius of the user's location. Here on the iPad simulator, the pins drop in the surrounding areas of Apple Headquarters.

Another really cool thing Stephen did was to distinguish between those amateur radio stations that have their own websites and those that do not. For the ones that have web sites, on the annotation view he includes a chevron which, when clicked, yields the web page. See Figures 9-34 and 9-35. This code is seen directly under the switch cases in the APRSstationParser.m file.

CS-201 Final Project—Stephen M. Moraco's app provides Annotations to appear when one clicks on a pin and where a linked website is on the APRS server, a blue chevron appears where one may click to go to the amateur radio station's website. In this case, amateur radio station KJ6EXD-7 does have a website.

Figure 9.34. CS-201 Final Project—Stephen M. Moraco's app provides Annotations to appear when one clicks on a pin and where a linked website is on the APRS server, a blue chevron appears where one may click to go to the amateur radio station's website. In this case, amateur radio station KJ6EXD-7 does have a website.

CS-201 Final Project—Stephen M. Moraco's App showing the KJ6EXD-7 website embedded in the iPad.

Figure 9.35. CS-201 Final Project—Stephen M. Moraco's App showing the KJ6EXD-7 website embedded in the iPad.

In the APRSmapViewController implementation file, Stephen includes, among other things, a bare-bones methodology to switch between map, satellite, and hybrid views. An example of this is seen when we show the closest radio station to the user, which, in simulator mode is Apple Headquarters. See Figure 9-36, where the view is in Hybrid mode.

-(IBAction)selectMapMode:(id)sender
{
     UISegmentedControl *scChooser = (UISegmentedControl *)sender;
     int nMapStyleIdx = [scChooser selectedSegmentIndex];
     NSLog(@"APRSmapViewController:selectMapMode - New Style=%d",nMapStyleIdx);

     switch (nMapStyleIdx) {
          case 0:
               self.mapView.mapType = MKMapTypeStandard;
               break;
          case 1:
               self.mapView.mapType = MKMapTypeSatellite;
               break;
          case 2:
               self.mapView.mapType = MKMapTypeHybrid;
               break;
          default:
               NSLog(@"APRSmapViewController:selectMapMode - Unknown Selection?!");
               break;
     }
}
CS-201 Final Project—Stephen M. Moraco's App showing the closest amateur radio station to Apple Headquarters in the Hybrid map view

Figure 9.36. CS-201 Final Project—Stephen M. Moraco's App showing the closest amateur radio station to Apple Headquarters in the Hybrid map view

Finally, as a nice finishing touch that I always encourage students to complete, Stephen included a nice About page in the AboutView nib. See Figure 9-37.

CS-201 Final Project—Stephen M. Moraco's App showing his "About Page" -- totally cool!

Figure 9.37. CS-201 Final Project—Stephen M. Moraco's App showing his "About Page" -- totally cool!

Note

In order to run the code, you will need to have a password and username. You have two options here: 1) Acquire your own or 2) Download any of these 3 apps, which are essentially the same.

http://itunes.apple.com/us/app/pocketpacket/id336500866?mt=8
     http://itunes.apple.com/us/app/ibcnu/id314134969?mt=8
     http://itunes.apple.com/us/app/aprs/id341511796?mt=8

Final Project—Example 2

Stephen A. Moraco is a gifted high school student who attended my class. His app parses the National Weather Cam network at http://www.mhartman-wx.com/wcn/. This can be seen in the TrafficCamParser implementation file static NSString *strURL =http://www.mhartman-wx.com/wcn/wcn_db.txt. See Figure 9-38.

CS-201 Final Project—Stephen A. Moraco's App starts off with hundreds of pins plummeting from the sky as they fill up a specified area around the user's "current" location at Apple Headquarters.

Figure 9.38. CS-201 Final Project—Stephen A. Moraco's App starts off with hundreds of pins plummeting from the sky as they fill up a specified area around the user's "current" location at Apple Headquarters.

He found that he needed to use an adapter to filter out bad meta tags in the <HEAD></HEAD> sections. There was so much extraneous matter on the server that it was crashing the code. To take care of this, he had to make rules to replace "^" with </field><field>, replace <br>'s with blank space, replace "<font size="−1">(" and ")<br>" with </field><field>, start and end with <CAM><field> and </field></CAM> and remove </font> tags, remove nonbreaking spaces. I've added numbering to help you see the start of each line, as the word wrap confuses me, too!

1.  NSString *strNoParaQueryResults = [strQueryResults
    stringByReplacingOccurrencesOfString:@"<font size="-1">(" withString:@"</field><field>"];
2.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@")<br>" withString:@"</field><field>"];
3.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"</font>" withString:@""];
4.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"&nbsp;" withString:@""];
5.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"></a>" withString:@"></img></a>"];
6.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"width=150" withString:@"width="150""];
7.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"height=100" withString:@"height="100""];
8.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"width=100" withString:@"width="100""];
9.  strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"height=150" withString:@"height="150""];
10. strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"border=0" withString:@"border="0""];
11. strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"""" withString:@"""];
12. strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@".jpg " withString:@".jpg" "];
13. strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"&" withString:@"and"];
14. strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"<font size="-1">" withString:@""];
15. strNoParaQueryResults = [strNoParaQueryResults
    stringByReplacingOccurrencesOfString:@"</b<" withString:@"</b<<"];

The TrafficCamAnnotation.h header files used is straightforward and simple, using the + (id)annotationWithCam:(TrafficCam *)Cam; and - (id)initWithCam:(TrafficCam *)Cam; pointers as described earlier for my hypothetical GratefuldeadParser.h. In this case, + (id)annotationWithCam:(TrafficCam *)Cam; creates parsed file and - (id)initWithCam:(TrafficCam *)Cam; initializes it. The result of all this hard work, taking care of the non-useful code, can be seen in clean annotation. See Figure 9-39.

#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>

@class TrafficCam;

@interface TrafficCamAnnotation : NSObject <MKAnnotation> {
     CLLocationCoordinate2D Coordinate;
     NSString *Title;
     NSString *Subtitle;
     TrafficCam *Cam;
}

@property(nonatomic, assign) CLLocationCoordinate2D coordinate;
@property(nonatomic, retain) NSString *title;
@property(nonatomic, retain) NSString *subtitle;
@property(nonatomic, retain) TrafficCam *cam;

+ (id)annotationWithCam:(TrafficCam *)Cam;
- (id)initWithCam:(TrafficCam *)Cam;

@end
CS-201 Final Project—Stephen A. Moraco's App zoomed into the Colorado Springs area. The annotation of North Academy at Shrider appears because the author clicked on that intersection.

Figure 9.39. CS-201 Final Project—Stephen A. Moraco's App zoomed into the Colorado Springs area. The annotation of North Academy at Shrider appears because the author clicked on that intersection.

Stephen also found he could not automatically use the camera video views. Working around this ended up being a non-trivial task in TrafficCamSettingsViewController.m. One example was to allow orientations other than the default portrait orientation:

BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

He had to arrange this code in order to have beautifully spaced video cam images fitting nicely in the screen, as illustrated in Figure 9-40.

CS-201 Final Project—Stephen A. Moraco's App zoomed into the Colorado Springs area. The annotation of North Academy at Shrider Appears because the author clicked on that intersection.

Figure 9.40. CS-201 Final Project—Stephen A. Moraco's App zoomed into the Colorado Springs area. The annotation of North Academy at Shrider Appears because the author clicked on that intersection.

Biographical Info—Example 3, Satish Rege

Why do I want to be an iPhone developer? Simple—the iPhone imparts the computing, the communicating, and the multimedia experience of a large computing system in the palm of your hand. It provides rich resources and user interface primitives to express creative capabilities in a synergistic way. These iPhone properties attracted me to want to learn iPhone development tools to express my own ideas. Rory's course was an excellent introduction that covered a multitude of iPhone capabilities and made them easy to master.

Satish Rege

Figure 9.41. Satish Rege

Final Project—Example 3

Satish (Figure 9-41) was always able to come up with eloquent and simple code for all his homework assignments. When I would grade the weekly assignments, Satish had the knack of always being able to put into 20 lines of code what others would often take three times as much to do the same thing. For his final project, Satish's app allows one to look up ahead at the traffic at intersections to come, and, if there is a traffic jam at one intersection, to recommend another.

At least in theory, that is how it works. Satish saves a lot of heartache by starting at one location he knew would be a tough intersection: I-25Northbound. He focused on controller implementation files and then he rotates back and forth from there depending on your location in Colorado Springs. He has 27 cases for the 27 cameras in Colorado Springs. Simple, elegant, ... beautiful.

Figure 9-42 shows the list. Figures 9-43 and 9-44 show two examples of the traffic views.

//Choose the camera depending on your co-ordinate

     switch (cameraCordinate) {
          case 1:
               url = [NSURL
URLWithString:@"http://www.springsgov.com/trafficeng/bImage.ASP?camID=17"];      //Camera
- S Academy/ I-25 North
               break;
           case 2:
                url = [NSURL
URLWithString:@"http://www.springsgov.com/trafficeng/bImage.ASP?camID=18"];      //Camera
- HWY 85/87/I-25 N
               break;

>>>>>>
>>>>>>
>>>>>>
          case 26:
               url = [NSURL
URLWithString:@"http://www.springsgov.com/trafficeng/bImage.ASP?camID=33"];     //
Camera - Monument/ I-25 N
               break;
          case 27:
               url = [NSURL
URLWithString:@"http://www.springsgov.com/trafficeng/bImage.ASP?camID=49"];      //Camera
- CountyLine/ I-25 SE
               break;
CS-201 Final Project—Rege's app selects the traffic lights closest to the user as he or she is driving down the street.

Figure 9.42. CS-201 Final Project—Rege's app selects the traffic lights closest to the user as he or she is driving down the street.

CS-201 Final Project—Rege's traffic monitoring app showing the embedded camera view.

Figure 9.43. CS-201 Final Project—Rege's traffic monitoring app showing the embedded camera view.

CS-201 Final Project—Satish Rege's app showing another embedded camera view.

Figure 9.44. CS-201 Final Project—Satish Rege's app showing another embedded camera view.

Zoom Out ... Seeing the Big Picture

It's important to know where we came from, where we are now, and where we are going next. Not to get too metaphysical on you, but this chapter is a bit of a metaphor for our lives. Where were you 5 years ago? Last year? One day before you bought this book? Where do you intend to be 6 months from now?

That's why this subject is so popular. People love to know where they are! People love to know, love to be shown, how to get from "here" to "there."

You know how men stereotypically refuse to stop and ask for directions? I know I do—because I should just know where I'm going. When GPS came on the scene, I was impressed. But when Apple included one, by way of 'Maps,' in my first iPhone, I was totally blown away. All of a sudden, I had the ability to consult the oracle and maintain my male ego at the same time!

That's power, and that's authority ... and that's the revolution you've joined. Now that you have completed this book, and successfully navigated through these exercises—some easier, some tougher—you are well on your way in the world of programming.

As I stated earlier, my goals for you in this chapter were humble. As in any really challenging and worthwhile pursuit, practice makes perfect. If you are exhausted, but still excited about these ideas and possibilities, then I count that as full success—for you and for myself.

Some of you are maybe thinking about topics that we did not cover in this book: the accelerometer, cameras/videos, peer-to-peer protocol, RSS feeds, mail clients/POP servers, etc. If these areas interest you, my hope is that your mind is already racing off in these new directions. That means you do know where you are, and that you know where you want to go. Life is good!

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

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