Nice MKAnnotation

姜淇
2023-12-01

This protocol allows us to detect changes in property values of objects and works as follows:

  • We have an object X of which we want to know when an attribute is modified
  • We have and object Y which will perform an action when X changes its attribute
  • The previous protocol allows the object Y to be notified of changes, for the attribute we specify, of the object X.

To achieve this we only need to tell X that Y wants to be notified of such changes using the method addObserver:forKeyPath:options:context: defined in the protocol and perform the appropriate actions using the methodobserveValueForKeyPath:ofObject:change:context: on the object Y, since this is the message that is sent when the attribute changes its value.

How example we use this method to detect when a user selects or deselects an annotation (MKAnnotation) on a map (MKMapView). First I will show how to set up the project as a tutorial and below how to used exactly the KVO, if you already have your project, go directly to the second section.

Preparation

First create a new project using the Xcode template based on a view. We add to the project the MapKit Framework to use the map and annotations.
Then modify the view controller to add an IBOutlet as reference the map that will be added later. We will have to import the Framework.

#import <UIKit/UIKit.h>;
#import <MapKit/MapKit.h>;
 
@interface MapKVOViewController : UIViewController  {
 
	IBOutlet MKMapView *theMapView;
}
 
@end

We open the file MapKVOViewController.xib with Interface Builder and add a MKMapView to assign the previously created IBoutlet, also indicate that the delegate of the map will be the controller.
For annotations on the map we need to add another class to our project, in Xcode add a new class from a NSObject, we import here also the MapKit Framework. This class has to comply with the protocol so modify the MKAnnotation.h to do so.

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
 
@interface MapAnnotation : NSObject {
	NSString *title;
	NSString *subtitle;
	CLLocationCoordinate2D coordinate;
}
 
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
 
// Title and subtitle for use by selection UI.
- (NSString *)title;
- (NSString *)subtitle;
 
@end

How this class is only for a test, initialize variables at constant values, modify the .m to look like this.

@implementation MapAnnotation
 
@synthesize coordinate;
 
- (id)init {
	[super init];
 
	coordinate.latitude = 25;
	coordinate.longitude = 25;
	title = [NSString stringWithFormat:@"A Testing annotation"];
	[title retain];
	subtitle = [NSString stringWithFormat:@"selecting detection"];
	[subtitle retain];
	return self;
}
 
- (NSString *)title {
	return title;
}
 
- (NSString *)subtitle{
	return subtitle;
}
 
- (void)dealloc{
	[title release];
	[subtitle release];
	[super dealloc];
}
 
@end

To end the project preparation we only need to add an annotation on the map, for example use the viewDidLoad to do so. Remember you have to import your new class to use annotations on the map (here MapAnnotation).

- (void)viewDidLoad {
    [super viewDidLoad];
	MapAnnotation *newAnnotation = [[MapAnnotation alloc] init];
	[theMapView addAnnotation:newAnnotation];
}

Using KVO

In our example, object X is the view of the annotation, the pin you see on the map, in one hand we have the annotation itself and in another the view that represents it on the map, as the action of selection is made on the map, is the view of the annotation which has an attribute called selected, which is what we will want to monitor.

The object Y, will be our MapKVOViewController, which is responsible for adding annotations to the map.

First step, tell the object X we want to observe the selected attribute, if we want to do this with all the entries in the map, a good place is the method MapView:didAddAnnotationViews: in MKMapViewDelegate protocol, add this method to our controller and modify it to be like this

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
	for (MKAnnotationView *anAnnotationView in views) {
		[anAnnotationView setCanShowCallout:YES];
		[anAnnotationView addObserver:self
						 forKeyPath:@"selected"
					              options:NSKeyValueObservingOptionNew
						      context:ANNOTATION_SELECTED_DESELECTED];
	}
}

Please note that this will be done for each annotation, so if there are different types of annotations, you have to differentiate between which one you want to be notified and which ones not.

The method that interests us is addObserver:forKeyPath:options:context:, to the latter we will indicate who will be the observer, self, which attribute to observe, selected, with which options, NSKeyValueObservingOptionNew indicates that we want the new value, and a context that is used to give us more information, such as what we are seeing, here you can use nil if you observe only one thing, if you have to distinguished it is useful to use constants for each case, here we use

static NSString* const ANNOTATION_SELECTED_DESELECTED = @"mapAnnotationSelectedOrDeselected";

defined at the beginning of our .m

Finally, in order to take any action, once notified of the change, we need to overwrite the method observeValueForKeyPath:ofObject:change:context:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
 
    NSString *action = (NSString *)context;
	if ([action isEqualToString:ANNOTATION_SELECTED_DESELECTED]) {
		BOOL annotationSelected = [[change valueForKey:@"new"] boolValue];
		if (annotationSelected) {
			NSLog(@"Annotation was selected, do whatever required");
                        // Accions when annotation selected
		}else {
			NSLog(@"Annotation was deselected, do what you must");
                        // Accions when annotation deselected
		}
	}
}
 类似资料:

相关阅读

相关文章

相关问答