Reputation: 7574
I have been having ongoing memory leak problems with MKMapView (reported to Apple, but no response), and so as a work-around I tried to just allocate one MKMapView and re-use that map instead of reallocating a new one. The problem I'm running into with this is I have yet to be able to reset the MKMapView to the view it starts with. I assume because I'm working from the USA, I initially get a view like this:
(source: cprince.com)
With this code:
static MKMapView *map = nil;
- (void) showUserLocation {
[map setShowsUserLocation:YES];
CLLocationCoordinate2D coord;
coord.latitude = 39.0; // changed from original coords
coord.longitude = -105.0;
MKCoordinateRegion r = MKCoordinateRegionMakeWithDistance(coord, 1000, 1000);
[map setRegion:r animated:YES];
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (! map) {
CGRect r = CGRectMake(20, 50, 196, 75);
map = [[MKMapView alloc] initWithFrame:r];
defaultRegion = MKCoordinateRegionMake(map.centerCoordinate, MKCoordinateSpanMake(180, 360));
} else {
[map setRegion:defaultRegion animated:YES];
}
[self.view addSubview:map];
}
- (IBAction)annotateMap:(id)sender {
[self showUserLocation];
}
when I click on the button that calls annotateMap, I get the following map (no surprises yet):
(source: cprince.com)
then, when I navigate away (using a navigation controller) from this view and then re-enter the view (viewWillAppear is again called, but this time it re-uses the MKMapView previously allocated), I get the following map view:
(source: cprince.com)
and of course I should be getting the original default region because of this part of the viewWillAppear:
defaultRegion = MKCoordinateRegionMake(map.centerCoordinate, MKCoordinateSpanMake(180, 360));
} else {
[map setRegion:defaultRegion animated:YES];
}
It doesn't matter if do the setRegion after the addSubview or before. That is, the change below doesn't correct the probelem:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
BOOL firstTime = NO;
if (! map) {
firstTime = YES;
CGRect r = CGRectMake(20, 50, 196, 75);
map = [[MKMapView alloc] initWithFrame:r];
defaultRegion = MKCoordinateRegionMake(map.centerCoordinate, MKCoordinateSpanMake(180, 360));
}
[self.view addSubview:map];
if (! firstTime) {
[map setRegion:defaultRegion animated:YES];
}
}
and using MKMapRectWorld doesn't help either:
defaultRegion = MKCoordinateRegionForMapRect(MKMapRectWorld);
The problem is also not due to some kind of race condition with MKMapView. If I alter the handler for the button:
- (IBAction)annotateMap:(id)sender {
defaultRegion = MKCoordinateRegionMake(map.centerCoordinate, MKCoordinateSpanMake(180, 360));
[self showUserLocation];
}
and wait until the map is settled on the USA view before I click the button, the problem is the same.
I am using iOS 6.1.2 and XCode 4.6.
(The full XCode project is available at http://www.cprince.com/stackoverflow/Map.zip).
Thoughts?
FIRST CHANGE
I made the defaultRegion a static instead of an instance variable and this doesn't change the situation markedly. (I reported the lat/long for the default region below). Here's the result in the map:
(source: cprince.com)
For clarity, the code as it stands now is:
static MKMapView *map = nil;
static MKCoordinateRegion defaultRegion;
- (void) showUserLocation {
[map setShowsUserLocation:YES];
CLLocationCoordinate2D coord;
coord.latitude = 39.0; // changed from original
coord.longitude = -105.0;
MKCoordinateRegion r = MKCoordinateRegionMakeWithDistance(coord, 1000, 1000);
[map setRegion:r animated:YES];
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
BOOL firstTime = NO;
if (! map) {
firstTime = YES;
CGRect r = CGRectMake(20, 50, 196, 75);
map = [[MKMapView alloc] initWithFrame:r];
defaultRegion = MKCoordinateRegionMake(map.centerCoordinate, MKCoordinateSpanMake(180, 360));
NSLog(@"defaultRegion: center: lat: %f, long: %f; span: %f dlat: dlong: %f", defaultRegion.center.latitude, defaultRegion.center.longitude, defaultRegion.span.latitudeDelta, defaultRegion.span.longitudeDelta);
//defaultRegion = MKCoordinateRegionForMapRect(MKMapRectWorld);
}
[self.view addSubview:map];
if (! firstTime) {
NSLog(@"defaultRegion: center: lat: %f, long: %f; span: %f dlat: dlong: %f", defaultRegion.center.latitude, defaultRegion.center.longitude, defaultRegion.span.latitudeDelta, defaultRegion.span.longitudeDelta);
[map setRegion:defaultRegion animated:YES];
}
}
- (IBAction)annotateMap:(id)sender {
//defaultRegion = MKCoordinateRegionMake(map.centerCoordinate, MKCoordinateSpanMake(180, 360));
[self showUserLocation];
}
Upvotes: 3
Views: 1552
Reputation: 437632
If you make your view controller a delegate
for the MKMapView
and implement regionDidChangeAnimated
, you'll see that when you first create a map view, the centerCoordinate
is 30, -40
, but it's subsequently set to 37.178181, -96.054581
(for U.S. users, at least). You apparently can't rely upon the region
and/or centerCoordinate
immediately after creating the map view. You have to give it a chance to reset its region
, which apparently happens asynchronously.
If you want to save the defaultRegion
after the MKMapView
sets it the first time, set the delegate
property of the MKMapView
and then implement:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
static BOOL firstTime = YES;
if (firstTime)
{
defaultRegion = mapView.region;
firstTime = NO;
}
}
Note, on viewWillDisappear
, since you've made your map a static
, make sure to nil
the map's delegate
(since that old view controller will disappear). If you want to continue to have your new instance of the view controller to be a MKMapViewDelegate
, then just set the delegate
again when you re-add the map view to your new instance of your detail view controller.
Old answer:
When you set defaultRegion
, that's an instance variable. Thus, that's lost when you dismiss the view. When you come back to the map (i.e. firstTime
is NO
), the defaultRegion
is never initialized, and thus is 0,0, and thus is in the middle of the Atlantic Ocean.
You're obviously not saving the defaultRegion
(generally you'd have a protocol to pass that back to the master view controller), which makes it hard for the app to know what to do when you select the item in the master view again.
Upvotes: 3