Search code examples
iphonekey-value-observing

Where to add and remove observers for KVO when presenting a DetailViewController


I have a MKMapView with a callout. When the callout (detail disclosure indicator) button is pressed, it shows a detailViewController. I want to add the ability to change the map type and pin color for my annotation. So I currently do this:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {

    DetailMapViewController *destination = [[DetailMapViewController alloc] initWithNibName:@"DetailMapViewController" bundle:nil];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:destination];

    if ([destination  respondsToSelector:@selector(setDelegate:)]) {
        [destination setValue:self forKey:@"delegate"];
    }
    if ([destination respondsToSelector:@selector(setPlacemark:)]) {
        [destination setValue:_currentPlacemark forKey:@"Placemark"];
    }
    if ([destination respondsToSelector:@selector(setMapTypeAsNum:)]) {
        [destination setValue:[NSNumber numberWithInteger:self.mapView.mapType] forKey:@"mapTypeAsNum"];

        [destination addObserver:self forKeyPath:@"mapTypeAsNum" options:NSKeyValueObservingOptionNew context:NULL];
    }
    if ([destination respondsToSelector:@selector(setColorAsNum:)]) {
        [destination setValue:[NSNumber numberWithInteger:MKPinAnnotationColorPurple] forKey:@"colorAsNum"];

        [destination addObserver:self forKeyPath:@"colorAsNum" options:NSKeyValueObservingOptionNew context:NULL];
    }
    [self presentModalViewController:navController animated:YES];
}

In my observeValueForKeyPath method:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    DetailMapViewController *destination = (DetailMapViewController *)object;
    [destination removeObserver:self forKeyPath:@"mapTypeAsNum"];
    [destination removeObserver:self forKeyPath:@"colorAsNum"];

    if ([keyPath isEqualToString:@"mapTypeAsNum"]) {
        if ([object isKindOfClass:[DetailMapViewController class]]) {
            id mapType = [change objectForKey:NSKeyValueChangeNewKey];
            self.mapView.mapType = [mapType integerValue];
        }
    }
    else if ([keyPath isEqualToString:@"colorAsNum"]) {
        if ([object isKindOfClass:[DetailMapViewController class]]) {
            id pinColor = [change objectForKey:NSKeyValueChangeNewKey];
            MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[self.mapView viewWithTag:10];
            annotationView.pinColor = [pinColor integerValue];
        }
    }
}

The problem with this is I end up leaking observation info according to the Console if I don't change the pin color or map type in the DetailViewController to call the observeValueForKeyPath method. So I was wondering where I am to add the observer and remove the observer in a scenario like this. In the past using NSNotificationCenter, I would remove the observer in dealloc, and add the observer just before presenting the detailViewController similar to the above code. But with KVO, I'm not sure how this works. Any thoughts? Thanks.


Solution

  • You should stop observing just before the modal view is closed.

    The best way to accomplish this is to have the modal view notify your MKMapView controller when it is ready to be closed and have MKMapView stop observing and then close the modal view.