Trouble adding Map Tab


#1

I attempted the Map Tab challenge, and I’m running into an issue with having the WhereamiViewController from displaying the MapView, Text field, and Activity Indicator. My solution was to create a UIViewController subclass with XIB for user interface, leveraging the MapView project from a previous chapter. I don’t think the problem is the solution but rather the implementation.

I’ve edit the Active Target to include MapKit.framework and CoreLocation.framework, added the aforementioned objects to the View, and connected those objects between View and File’s Owner appropriately.

I’ll admit that while the book covers iPhone development, I’ve instead used the instruction using the iPad framework (which I assumed is virtually the same except for how it is deployed). I ran into this same issue with CurrentTimeView. While the view outlet was automatically connected to the instance of UIView, it wouldn’t display the button and label until I reconnected view to UIView.

I thought this would be the problem with the Map Tab, but the same fix didn’t work. Before I start pasting code, I figured I’d start with this in case there is an “is it plugged in?” solution to my problem. :confused:

Thanks,
jyanji


#2

I forgot to mention that I also replicated the steps to include the third tab. The tab is there, but it pulls up a blank view. I’m just looking to see what are some reasons why it would be blank despite the steps I’ve mentioned. Maybe my solution is the problem, hey? :wink:


#3

Oh! Nevermind. I reviewed my code this afternoon and found that I plugged my code into the wrong place.

I was originally attempting to do this:

- (void)loadView
{
	// Create location manager object - 
	locationManager = [[CLLocationManager alloc] init];
	
	// Make this instance of WhereAmIAppDelegate the delegate
	// it will send its messages to our WhereAmIAppDelegate
	[locationManager setDelegate:self];
	
	// We want all results from the location manager
	[locationManager setDistanceFilter:kCLDistanceFilterNone];
	
	// And we want it to be as accurate as possible
	// regardless of how much time/power it takes
	[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
	
	// Tell our manager to start looking for its location immediately
	//[locationManager startUpdatingLocation];
	[mapView setShowsUserLocation:YES];
}

When I meant to do this:

// This method gets called automatically when the view is created
- (void)viewDidLoad
{
	[super viewDidLoad];

	// Create location manager object - 
	locationManager = [[CLLocationManager alloc] init];
	
	// Make this instance of WhereAmIAppDelegate the delegate
	// it will send its messages to our WhereAmIAppDelegate
	[locationManager setDelegate:self];
	
	// We want all results from the location manager
	[locationManager setDistanceFilter:kCLDistanceFilterNone];
	
	// And we want it to be as accurate as possible
	// regardless of how much time/power it takes
	[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
	
	// Tell our manager to start looking for its location immediately
	//[locationManager startUpdatingLocation];
	[mapView setShowsUserLocation:YES];
}

I now have a Map Tab that functions exactly like the Whereami project. :smiley:


#4

The map challenge is making me a thousand times crazy. I’m getting the blank Map tab along with the error: “Class WhereamiViewController does not implement the CLLocationManagerDelegate protocol”. Here’s where I am so far. What am I missing? Joe, can you please give a hint or fill in the gaps?

  1. New view controller with XIB named WhereamiViewController

  2. Set the active targets and import the frameworks in HypnoTimeAppDelegate.h:

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

@interface HypnoTimeAppDelegate : NSObject <UIApplicationDelegate, 
CLLocationManagerDelegate, MKMapViewDelegate> {
    
	UIWindow *window;
	UITabBarController *tabBarController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end
  1. Update HypnoTimeAppDelegate.m
#import "WhereamiViewController.h"
...
	UIViewController *vc3 = [[WhereamiViewController alloc] init];
	
	// make an array containing the two view controllers
	NSArray *viewControllers = [NSArray arrayWithObjects:vc1, vc2, vc3, nil];
	
	[vc1 release];
	[vc2 release];
	[vc3 release];
  1. Construct the MKMapView interface in Interface Builder and connect the mapView, activityIndicator, and locationTitleField outlets to File’s Owner.

  2. Add the following to WhereamiViewController.m:

#import "WhereamiViewController.h"

@implementation WhereamiViewController

-(id)init
{
	// call the superclass's designated initializer
	[super initWithNibName:@"WhereamiViewController" bundle:nil];
	//[super initWithNibName:nil bundle:nil];

	
	// get the tab bar item
	UITabBarItem *tbi = [self tabBarItem];
	
	// give it a label
	[tbi setTitle:@"Map"];
	
	// create a UIImage from a file
	UIImage *i = [UIImage imageNamed:@"Map.png"];
	
	// add to tab bar
	[tbi setImage:i];
	
	return self;
}

 // The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
    return [self init];
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad 
{
    [super viewDidLoad];
	
	// Create location manager object - 
	locationManager = [[CLLocationManager alloc] init];
	
	// Make this instance of WhereAmIAppDelegate the delegate
	// it will send its messages to our WhereAmIAppDelegate
	[locationManager setDelegate:self];
	
	// We want all results from the location manager
	[locationManager setDistanceFilter:kCLDistanceFilterNone];
	
	// And we want it to be as accurate as possible
	// regardless of how much time/power it takes
	[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
	
	// Tell our manager to start looking for its location immediately
	//[locationManager startUpdatingLocation];
	[mapView setShowsUserLocation:YES];
}
	
  1. Add to WhereamiViewController.h:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
@class MapPoint;


@interface WhereamiViewController : UIViewController {
	
	CLLocationManager *locationManager;
	
	IBOutlet MKMapView *mapView;
	IBOutlet UIActivityIndicatorView *activityIndicator;
	IBOutlet UITextField *locationTitleField;

}

@end
  1. ???

#5

I followed you example and solved the callenge. Map Tab now is working fine. I only used delegate protocols
(CLLocationManagerDelegate and MKMapViewDelegate) in WhereamiViewController, instead of HypnoTimeAppDelegate.


#6

[quote=“josenet”]I followed you example and solved the callenge. Map Tab now is working fine. I only used delegate protocols
(CLLocationManagerDelegate and MKMapViewDelegate) in WhereamiViewController, instead of HypnoTimeAppDelegate.[/quote]

When you say you only used the CCLocationManagerDelegate and MKMapViewDelegate in WhereamiViewController, where did you “implement” them? I have all of the code in; but when I goto link the MapView and activityIndicator in the interface builder, and ctrl+click, none of my IBOutlets show up, this is where I am confused.


#7

@josenet: You are correct. The first few pages of Chapter 8 helped a lot.
@Ryang1428: In IB, connect the view outlet to View in File’s Owner.

I tried to get the entire Whereami app in the map tab. Everthing except the location field search is working. It uses the MapPoint files and shows a pin on the user’s location. It’s not perfect, but it compiles. Here are my revisions:

  1. Set the active targets and import the frameworks in WhereamiViewController.h:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
@class MapPoint;

//declare that the class conforms to the map delegate protocols 
@interface WhereamiViewController : UIViewController <UIApplicationDelegate, UITextViewDelegate, CLLocationManagerDelegate, MKMapViewDelegate> {
	
	UIWindow *window;
	
	CLLocationManager *locationManager;
	
	IBOutlet MKMapView *mapView;
	IBOutlet UIActivityIndicatorView *activityIndicator;
	IBOutlet UITextField *locationTitleField;

}

@property (nonatomic, retain) IBOutlet UIWindow *window;

- (void)findLocation;
- (void)foundLocation;

@end
  1. Add the following to WhereamiViewController.m:
#import "WhereamiViewController.h"
#import "MapPoint.h"


@implementation WhereamiViewController

@synthesize window;

-(id)init
{
	// call the superclass's designated initializer
	[super initWithNibName:@"WhereamiViewController" bundle:nil];

	
	// get the tab bar item
	UITabBarItem *tbi = [self tabBarItem];
	
	// give it a label
	[tbi setTitle:@"Map"];
	
	// create a UIImage from a file
	UIImage *i = [UIImage imageNamed:@"Map.png"];
	
	// add to tab bar
	[tbi setImage:i];
	
	return self;
}


 // The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
    return [self init];
}



// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad 
{
    [super viewDidLoad];
	
	// Create location manager object - 
	locationManager = [[CLLocationManager alloc] init];
	
	// Make this instance of WhereAmIAppDelegate the delegate
	// it will send its messages to our WhereAmIAppDelegate
	[locationManager setDelegate:self];
	
	// assign the mapview delegate for zoom and show location
	[mapView setDelegate:self];
	
	
	// We want all results from the location manager
	[locationManager setDistanceFilter:kCLDistanceFilterNone];
	
	// And we want it to be as accurate as possible
	// regardless of how much time/power it takes
	[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
	
	// Tell our manager to start looking for its location immediately
	[locationManager startUpdatingLocation];
	[mapView setShowsUserLocation:YES];
	
}

- (void)findLocation
{
	[locationManager startUpdatingLocation];
	[activityIndicator startAnimating];
	[locationTitleField setHidden:YES];
}
- (void)foundLocation
{
	[locationTitleField setText:@""];
	[activityIndicator stopAnimating];
	[locationTitleField setHidden:NO];
	[locationManager stopUpdatingLocation];
}

- (BOOL)textFieldShouldReturn:(UITextField *)tf
{
	[self findLocation];
	[tf resignFirstResponder];
	return YES;
}
- (void)mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *)views
{
    MKAnnotationView *annotationView = [views objectAtIndex:0];
    id <MKAnnotation> mp = [annotationView annotation];
    MKCoordinateRegion region = 
	MKCoordinateRegionMakeWithDistance([mp coordinate], 250, 250);
    [mv setRegion:region animated:YES];
}
- (void)locationManager:(CLLocationManager *)manager
	   didFailWithError:(NSError *)error
{
	NSLog(@"Could not find location: %@", error);
	[self foundLocation];
}
- (void)locationManager:(CLLocationManager *)manager 
	didUpdateToLocation:(CLLocation *)newLocation 
		   fromLocation:(CLLocation *)oldLocation
{
    NSLog(@"%@", newLocation);
	
    // How many seconds ago was this new location created?
    NSTimeInterval t = [[newLocation timestamp] timeIntervalSinceNow];
    // CLLocationManagers will return the last found location of the 
    // device first, you don't want that data in this case.
    // If this location was made was more than 3 minutes ago, ignore it. 
    if (t < -180) {
        // This is cached data, you don't want it, keep looking
        return;
    }
    MapPoint *mp = [[MapPoint alloc] 
					initWithCoordinate:[newLocation coordinate] 
					title:[locationTitleField text]];
    [mapView addAnnotation:mp];
    [mp release];
	
    [self foundLocation];
}


/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
	
}


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


@end
  1. Drag copies of MapPoint.h and MapPoint.m from the Whereami app into HypnoTime (the same way we did on p. 104 for HypnosisView)

#8

** UPDATE TO MESSAGE BELOW: I figured out the problem; I had forgotten to add the frameworks (via the Project > Edit Active Target menu). Added CoreLocation and MapKit and things are fine now. I’m leaving my original message here in case anyone else runs into something similar.

Argh. I’m pulling my hair out over here. I’ve set up a 3rd tab to use the MapViewController ViewController object, and I’m getting a linker error:

[quote]Undefined symbols:
"_kCLDistanceFilterNone", referenced from:
_kCLDistanceFilterNone$non_lazy_ptr in MapViewController.o
(maybe you meant: _kCLDistanceFilterNone$non_lazy_ptr)
"_kCLLocationAccuracyBest", referenced from:
_kCLLocationAccuracyBest$non_lazy_ptr in MapViewController.o
(maybe you meant: _kCLLocationAccuracyBest$non_lazy_ptr)
OBJC_CLASS$_CLLocationManager”, referenced from:
objc-class-ref-to-CLLocationManager in MapViewController.o
ld: symbol(s) not found
[/quote]
This makes things sound like the issue is that the linker doesn’t know about the Core Location constants, but here is my entire MapViewController.h:

[code]#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface MapViewController : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>
{
CLLocationManager *locationManager;
IBOutlet MKMapView *mapView;
}

@end
[/code]

As you can see right there at the top, I have the #import of the CoreLocation headers file… so why does it not know the constants? Or is something else going on?