"

Posts Tagged ‘UIKit’

New iPhone apps with Augmented Reality

Tuesday, October 27, 2009 @ 09:10 PM
posted by Geppy

We just completed the upload of 5 new iPhone Apps on the AppStore. The 5 apps are

  • Milano Metro AR
  • Madrid Metro AR
  • Roma Metro AR
  • Barcelona Metro AR
  • Valencia Metro AR
Madrid Metro AR is an augmented reality app for travelers with iPhone 3Gs. It helps you to quickly locate the closest subway stations.
After launching the app, the Madrid subway stops are displayed onto a map of the city together with your current location. Select the closest station and an arrow will help you indicating the right direction.
Then, when you lift the iPhone in front of you, you will be able to see the direction and the distance to subway stations nearby while turning around.
While you watch through your iPhone camera, you will see the subway stops superimposed, the name and the distance to reach them to ease your subway traveling.
These are apps with augmented reality for travelers with iPhone 3Gs. They help you to quickly locate the closest subway stations.
After launching the app, the subway stops are displayed onto a map of the city together with your current location. Selecting the closest station, an arrow will help you indicating the right direction.
Metro Madrid MapMadrid Metro Arrow
Then, when you lift the iPhone in front of you, you will be able to see the direction and the distance to subway stations nearby while turning around.
While you watch through your iPhone camera, you will see the subway stops superimposed, the name and the distance to reach them to ease your subway traveling.
Madrid Metro AR

Here the 5 wonderful icons:

Stay tuned. Soon we will publish the iTunes links.

Post to Twitter Post to Facebook

During the different SDK releases, Apple has changed many things. Now, developers can build their own applications using different approaches, but I personally believe that an intensive use of Interface Builder is a good strategy to set up in few minutes a project and easily maintain it.

During my trainings, I collected the request of my students and most of the time, they wish to use more and more Interface Builder, because it is more intuitive and easier to use than simply typing code.

In this post, I want to explain how to build the infrastructure for the UINavigationController using Interface Builder. I think this is the best, cleanest and quickest way to build an app. If you just repeat this exercise a couple of times to understand the concept, you will be able to build a navigation controller-based app in few minutes.

To better understand what we do, I will start from an window-based project without taking advantage of the view-based template prepared by Apple. So, open Xcode and create a new project. Select a Window-based Application template and name it MyNav. We could have chosen the View-based Application template and Xcode would have setup the whole infrastructure for us, but then you loose all the magic behind it. So, I prefer you build it from the scratch, so that you will learn how it works and you can easily modify it to create more complex functionalities.

Give a look at the created project. It contains three files: MyNavAppDelegate.h, MyNavAppDelegate.m and MainWindow.xib. We need to modify these three files to go on, but let us start with some design.

Suppose you want to navigate 3 views: view1, view2 and view3. The view1 is the view appearing as soon as the app finishes launching. This is called the main view. Since it is the first view in the sequence, view1 is also called the root view. Root view and main View do not always coincide. For example, in the Contacts app, the main view is the All Contacts list, while the Root View is the Groups view.

Each view of our project will be a UIViewController and the Navigation Controller will be responsible to push or pop the next or the previous view onto the screen as they were in a stack. So, let us start.

Modify the MyNavAppDelegate.h file, so that it looks as follows:

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>

@interface MyNavAppDelegate : NSObject
{
 IBOutlet UIWindow *window;
 IBOutlet UINavigationController *myNavController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *myNavController;

@end

Now, let us modify the implementation file. Edit the MyNavAppDelegate.m file like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "MyNavAppDelegate.h"

@implementation MyNavAppDelegate
@synthesize window;
@synthesize myNavController;

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
 [window addSubview:myNavController.view];
 [window makeKeyAndVisible];
}
- (void)dealloc
{
 [myNavController release];
 [window release];
 [super dealloc];
}
@end

Now, fire up Interface Builder, double-clicking the MainWindow.xib file. From the Library drag a Navigation Controller and drop it in the MainWindow.xib.

Control-click the My Nav App Delegate object in the MainWindow.xib window and, when the HUD window opens, connect the myNavController outlet with the recently added Navigation Controller.
So, what have we done in the last part? It is very simple: we declared in the @interface of the MyNavAppDelegate a UINavigationController outlet. In the @implementation, we should have allocated and initialized this UINavigationController, but to avoid this, we added a Navigation Controller object with Interface Builder to our project. In this way, the XIB file contains the Navigation Controller. When we launch the app, the MainWindow.xib file, containing the Navigation Controller, is automatically loaded. Since this is a generic object to let Xcode understand that we are referring to the same Navigation Controller defined in the @interface, we need to connect the outlet of the App Delegate to the UINavigationController.

Now, we need to add the 3 UIViewController(s) to manage each view in the stack. The use of the UIViewController instead of simple UIView is requested by the fact that a UIViewController is a controller and thus, it is able to manage complex functionalities, such as screen rotations, that maybe you will use later.

The approach to setup a UIViewController and its XIB file is the same for each view, so I show you how to do it only for one of them. Then, you simply repeat the following steps:

  • Right-click the Classes group in the project window of Xcode and select Add->New File…
  • Select the UIViewController subclass template from the Cocoa Touch Classes and click Next.
  • Name the view FirstViewController (make sure that the Also Created flag is selected) and click Finish. You get two files (FirstViewController.h and FirstViewController.m).
  • Right-click again the Classes group and choose Add -> New File… This time, select View XIB template from the User Interface under iPhone.
  • Click Next and name the file FirstViewController.xib.
  • Click Finish.

Repeat the last paragraph for the second and third view (name them SeconViewController and ThirdViewController).

When you have the three controllers, double click the FirstViewController.xib. The MainWindow.xib window contains 3 objects: File’s Owner, First Responder and View. The File’s Owner is a Proxy Object: it simply represents in IB an object that you have created somewhere else (coding or using IB). We need to specify which object is exactly. To do this, select the File’s Owner and the Identity pane in the Interface Builder Inspector (Tools -> Identity Inspector). Choose as Class name FirstViewController. Repeat this step for the SecondViewController and for the ThirdViewController. When done, you have clearly associated each class to its XIB file.

Now, our File’s Owner represents the FirstViewController object. Right-click it and connect the view outlet with the View object. Open the View object (double-click it) and select the Attributes pane in the Inspector (Tools -> Attributes Inspector). Click on the Background color picker and change the color to red, so that we can distinguish it from the other views we are going to set up.
Now, we have to tell the Navigation Controller that the FirstViewController is the first view we want to load (the Main View, remember?) and use it as root view. Here, there is a small trick. Open again the MainWindow.xib file and double click the Navigation Controller. Click in the window of the Navigation Controller and select the Identity Inspector.
Select as class name FirstViewController. Then, select the Attributes Pane and select FirstViewController as NIB Name. The Navigation Controller should show now a link to the FirstViewController.xib as shown here on the right-hand side. Before you can Build and Run, choose the Detailed View of the MainWindow.xib. Click on the triangle close to the UINavigationController. Inside it, there is UIViewController. You need to make sure that this is is the right one. Select it and set its class name to FirstViewController in the Identity Inspector. In the same way, set to FirstViewController, the XIB Name to FirstViewController in the Attributes Inspector. If you miss these steps, your app will not work. It should look like in the following picture.

You can now Build and Run. You will get a window with a NavigationBar and red view. Let’s go back to the project.
So, we have set our root view. Now, we have to inform each view about the next view of the desired view sequence. You will see that many functionalities are for free and the code we are going to write is represented by few lines.
Edit the FirstViewController.h so that it looks like this:

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>

@class SecondViewController;

@interface FirstViewController : UIViewController
{
 IBOutlet SecondViewController *mySecondViewController;
}
@property (nonatomic, retain) IBOutlet SecondViewController *mySecondViewController;

@end

Remember to synthesize the property and add the following header #import “SecondViewController.h” in the FirstViewController.m file.

Now, open the FirstViewController.xib and add a UIViewController to the MainWindow.xib FirstViewController.xib window. Select this view controller and set its class name to SecondViewController in the Identity Inspector. Then, set also to SecondViewController the XIB Name in the Attributes Inspector. Now, finally, connect the File’s Owner outlet mySecondViewController to the view controller.

We do the same for the second view controller. Before that, open the SecondViewController.xib file and add a UIViewController object to the MainWindow.xib SecondViewController.xib window. Select this view controller and set its class to the ThirdViewController in the Identity Inspector and set its XIB Name to ThirdViewController in the Attributes Inspector.
Now, open the SecondViewController.h file and modify it as follows:

1
2
3
4
5
6
7
8
9
10
#import <UIKit/UIKit.h>

@class ThirdViewController;

@interface SecondViewController : UIViewController
{
 IBOutlet ThirdViewController *myThirdViewController;
}
@property (nonatomic, retain) IBOutlet ThirdViewController *myThirdViewController;
@end

Remember to synthesize the property and add the following header #import “ThirdViewController.h” in the SecondViewController.m file.

Now, build and run it. You don’t see any difference with the previous run. The infrastructure is already set up, but we need to add some way to push the next view to the screen. We can do it in different way. We choose the simplest one, using a push button in the center of the current view.
Add a button to the first and second view controllers. For each of them, add the following action in the header file of the corresponding class:

1
- (IBAction)bringNextView:(id)sender;

In the implementation, add the following:

1
2
3
4
- (IBAction)bringNextView:(id)sender
{
 [[self navigationController] pushViewController:mySecondViewController animated:YES];
}

Repeat the previous code for the second view controller. Don’t forget to connect the button to the IBAction of the File’s Owner. I attach the project MyNav.zip. I only add small things to make it to like nicer. If you have any question, simply contact me.

Have a nice coding!!!

Post to Twitter Post to Facebook

Adding maps to your iPhone app (MapKit)

Sunday, July 19, 2009 @ 12:07 PM
posted by Geppy

Last June, Apple released the iPhone SDK 3.0 with thousands of new fantastic features: copy and paste, magnetometer, undo and redo, GameKit, MapKit, and so on. I was immediately attracted by the MapKit, since I was waiting for it for a long time and, finally I got it.

The MapKit framework allows you to integrate a quite fully-featured map in your applications. In this way, your user won’t need to quit your app to check the geographical information in the Maps app. In reality a feature is missed: the possibility to provide a path between 2 points. You can currently do it, if you already have the path from some other sources, but you cannot retrieve that information from Google.

Let’s give a look at the MapKit and have some fun with the maps. Using Interface Builder you can embed a map in your app in few seconds. It is enough you just add a MKMapView object wherever you need and that’s it. But let’s try together.

Create a new View-based project and name it “Mapper”. The first thing we need to do is to add the MapKit framework. Go to Project -> Edit Active Target and choose the General tab. There, add the MapKit.framework to the Linked Libraries pressing the “+” button. Now, double click the MapperViewController.xib file. Then, drag a Map View object from the Library and drop it onto the View. Save and quit IB. Now, go back to Xcode and select Build and Run from the Build menu.

Interface Builder Inspector

Do you like it? You got a fully featured map. You can zoom in and out and you can pan left and right and up and down. Now, re-open the MapperViewController.xib file and select the MapView you previously added to it. Open the Inspector and go to the Attributes pane.

As you can see, you can change different map attributes. For example, you can change the map type to Map, Satellite and Hybrid. You can allow the zooming and the scrolling and you can also activate the current user location. You can change these features in IB, but you can also change them programmatically. To do so, we need to create an outlet in your class and connect it to the map. Open MapperViewController.h and import the MapKit header. Add this line

1
#import <MapKit/MapKit.h>

Then, add the following outlet:

1
IBOutlet MKMapView *mapView;

Create also a property:

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

Remember to synthesize the property and don’t forget to release it in the dealloc method. Now, open MapperViewController.xib and connect the File’s Owner outlet (mapView) with the map object you previously added (see Figure below).

Now, you can go back to the code (after saving in IB) and add the following lines inside the viewDidiLoad method of the MapperViewController.m:

1
2
3
4
5
6
7
-(void) viewDidLoad
{
    [super viewDidLoad];
    [mapView setMapType:MKMapTypeStandard];
    [mapView setZoomEnabled:YES];
    [mapView setScrollEnabled:YES];
}

Setting the Region

If you want to represent a specific portion of the map with a specific zoom level, you need to define a Map Region. This is a C structure composed by the Center and the Span. The Center is represented by the Longitude and the Latitude of the Region center, while the Span defines how much of the map is visible and is also the zoom level. A large Span corresponds to a low zoom level, while a small Span corresponds to a high zoom level.

Let’s define the Region for our map. At the end of the viewDidLoad method, add the following code:

1
2
3
4
5
6
MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };
region.center.latitude = 41.902245099708516;
region.center.longitude = 12.457906007766724;
region.span.longitudeDelta = 0.01f;
region.span.latitudeDelta = 0.01f;
[mapView setRegion:region animated:YES];

The region is defined around the San Peter Cathedral in Rome. Now, if you want to pan or zoom programmatically, you just need to change programmatically the Region. If you change the Span, you zoom. If you change the Center, you pan. Simple, no?

User Location

This is very simple too. If you want to show the user location, you just set the flag in IB, as previously discussed, or you just add the following line of code:

1
[mapView setShowsUserLocation:YES];

Converting coordinates

It is often useful to convert the geographical coordinate to the view coordinates and vice versa. You can do this, using these methods:

1
2
3
4
– convertCoordinate:toPointToView:
– convertPoint:toCoordinateFromView:
– convertRegion:toRectToView:
- convertRect:toRegionFromView:

Annotations

A map without any useful information on it is most of the time useless. The magic happens when the map helps you to find some service or some friend or when it shows any interesting information to your app. If you want to add information to the map you need to add annotations.
In the MapKit framework, an annotation is composed by two parts: an annotation object and an annotation view. The Annotation Object contains information related to the geographical coordinates, while the Annotation View represents the view associated to the annotation object that will be displayed on request.
An annotation object is any object that conforms to the MKAnnotation protocol. Let’s add an annotation object to our map. We need to define a new class. Right-click the Classes Group and select Add->New Files.

Select an Objective-C class as Subclass of NSObject and name it MyAnnotation. Open the MyAnnotation.h and add the bold code:

1
2
3
4
5
6
7
8
9
10
11
12
13
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>

@interface MyAnnotation : NSObject
{
     CLLocationCoordinate2D coordinate;
     NSString *title;
    NSString *subtitle;
}
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@end

Now, switch to the implementation file and add:

1
2
3
4
5
6
7
8
9
10
11
12
13
#import "MyAnnotation.h"

@implementation MyAnnotation

@synthesize coordinate, title, subtitle;

-(void)dealloc
{
    [title release];
    [subtitle release];
    [super dealloc];
}
@end

We have now our annotation class with the coordinates, a title and a subtitle for the Callout view (see below). Let’s instantiate an object and add it to the map.
Still in the viewDidLoad method add the following lines of code:

1
2
3
4
5
MyAnnotation *ann = [[MyAnnotation alloc] init];
ann.title = @"Rome";
ann.subtitle = @"San Peter";
ann.coordinate = region.center;
[mapView addAnnotation:ann];

Here, we created an ann object and set its title, subtitle and coordinates (for simplicity, I am setting the annotation coordinates to the map center, but you can put there any coordinates you like). You can create an array of annotations and then add it to the map.

Finally, we need to set our MapperViewController class as the delegate of the mapView. So, just add this line of code at the end of the viewDidLoad:

1
[mapView setDelegate:self];

and add the protocol name in the interface of the class:

1
@interface MapperViewController : UIViewController

The last step is to implement a delegate method that manages the annotations during the panning and zooming. The approach is very similar to the UITableView. We create a static identifier and we try to reuse the annotation view with the same identifier, as we do with the cell of the tableview. If we are not able to dequeue an annotation view, we need to allocate one. Then, we choose the pin color and allow a callout view. Then, we animate the drop of the pin.
Add the following code to the MapperViewController.m file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id )annotation
{
    MKPinAnnotationView *pinView = nil;
    if(annotation != mapView.userLocation)
    {
         static NSString *defaultPinID = @"com.invasivecode.pin";
         pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
         if ( pinView == nil )
            pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
        pinView.pinColor = MKPinAnnotationColorPurple;
        pinView.canShowCallout = YES;
        pinView.animatesDrop = YES;
    }
    else
        [mapView.userLocation setTitle:@"I am here"];
    return pinView;
}

That’s it. Below, you can see the result and Here you find the Xcode project.

Post to Twitter Post to Facebook