My Map Tab Challenge Implementation


#1

Hi!
I have finished my implementation for the map tab challenge. I’ve reused the MyLocation code with minor changes. All is working fine (maybe I missed some release stuff) but the ActivityIndicator, it doesn’t dissapear when the new location is found. Could you help me about that?

//
//  MyLocationViewController.h
//  HypnoTime
//
//  Created by Jose Dueñas on 09/08/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

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

@interface MyLocationViewController : UIViewController <CLLocationManagerDelegate, MKMapViewDelegate>
{
    CLLocationManager *locationManager;
    
    IBOutlet MKMapView *worldView;
    IBOutlet UIActivityIndicatorView *activityIndicator;
}

- (void)findLocation;
- (void)foundLocation:(CLLocation *)loc;

@end
//
//  MyLocationViewController.m
//  HypnoTime
//
//  Created by Jose Dueñas on 09/08/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "MyLocationViewController.h"


@implementation MyLocationViewController

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
    self = [super initWithNibName:@"MyLocationViewController" bundle:nil];
    if (self) {
        // Get the tab bar item
        UITabBarItem *tbi = [self tabBarItem];
        
        // Give it a label
        [tbi setTitle:@"My Location"];
        
        // Create an image on the tab bar item
        UIImage *i = [UIImage imageNamed:@"Hypno.png"];
        
        // Put the image on the tab bar item
        [tbi setImage:i];
        
        locationManager = [[CLLocationManager alloc] init];
        [locationManager setDelegate:self];
        
        [locationManager setDistanceFilter:kCLDistanceFilterNone];
        [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
        
        // [locationManager startUpdatingLocation];
        [worldView setShowsUserLocation:YES];
    }
    return self;
}

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

- (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.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}

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

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


- (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 more than 3 minutes ago, ignore it.
    if (t < -180) {
        // This is cached data, you don't want it, keep looking
        return;
    }
    [self foundLocation:newLocation];
}

- (void)locationManager:(CLLocationManager *)manager 
       didFailWithError:(NSError *)error
{
    NSLog(@"Could not find location: %@", error);
}

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

- (void)findLocation
{
    [locationManager startUpdatingLocation];
    [activityIndicator startAnimating];
}

- (void)foundLocation:(CLLocation *)loc
{
    CLLocationCoordinate2D coord = [loc coordinate];
   
    // Zoom the region to this location
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 250, 250);
    [worldView setRegion:region animated:YES];
    
    [activityIndicator stopAnimating];
    [locationManager stopUpdatingLocation];
}
@end

And finally in HypnoTimeAppDelegate I have added:

UIViewController *vc3 = [[MapViewController alloc] init];
NSArray *viewControllers = [NSArray arrayWithObjects:vc1, vc2, vc3, nil];
[vc3 release];


#2

That’s one of the downsides of Mac and iOS programming; not all behaviour is in the code.

See the middle of page 102 (2nd ed.; bottom of page 77 in the 1st ed.) for how to do this.


#3

Hi gc3182,
thanks for your reply. I had that option (Hides when stop) enabled. The problem was when I run the application in the simulator. If I run the app in my own iPhone 3G, the activityIndicator is hidden when the location is found. So it seems to be a problem with simulator.

Regards,
Jose


#4

I don’t have the book in front of me at the moment, so I don’t recall exactly what the challenge was, but I added the map to the tab view controller in HypnoTime and an activity indicator as well but no search box or annotations. I started the activity indicator animating in -viewDidLoad and stopped it in -mapView:didUpdateUserLocation:. (We didn’t do that before; I just did it to force it to start spinning.) This worked fine in the simulator; it became visible, spun, then stopped and hid when the map showed Apple. (I’m running Xcode 4.0.2 on Snow Leopard 10.6.8 with iOS 4.3.)

One thing I noticed was that you still have -findLocation defined (which in Whereami was triggered when the user entered text), and I think that’s the only place activityIndicator is set to start animating. Since I didn’t see any indication of the text field in your code, I assume one isn’t there.

I didn’t add a text field either. Because of this, at least in my app, -locationManager:didUpdateToLocation:fromLocation: is never invoked either, so neither is -foundLocation:, which (in your code) is the only place the activityIndicator is told to stop animating.

All of which means, if I’m understanding correctly, that the activity indicator never started animating either. It’s just sitting there, visible, from the time you hit the Map tab.

Can you go back to the .xib? On mine, as soon as I checked “Hides When Stopped”, it automatically checked the “Hidden” box in the section below. Can you verify that BOTH checkboxes are checked for you?


#5

[quote=“gc3182”]
Can you go back to the .xib? On mine, as soon as I checked “Hides When Stopped”, it automatically checked the “Hidden” box in the section below. Can you verify that BOTH checkboxes are checked for you?[/quote]

Hi again,
yes, both are checked. Anyway, in my iPhone is working fine (activityIndicator is animating until it retrieve my current location, in that moment, the activityIndicator goes hidden). So it has to be a weird problem with my simulator.

Thanks for your help!
Jose


#6

Thanks for the feedback. Odd; sorry I couldn’t be more help.

If nothing else, you could force the activityIndicator to be hidden since “hides when stopped” is really just a convenience setting. When you want to hide it (i.e., right after -stopAnimating), use [activityIndicator setHidden: YES];. You can even log the value of [activityIndicator isHidden] to see whether the app thinks it’s hidden.

I understand that it’s working fine on the device (which in the end is what really matters) but it may still be useful to see exactly where the issue is.


#7

I believe this is due to the fact that the simulator returns a location immediately, so the time between showing and hiding the activity indicator is so close to zero that you won’t see it.


#8

Yes Joe, after many investigations I’ve reached to the same conclusion. Thanks for your reply.