Hoy veremos un ejemplo muy sencillo de cómo usar y para que sirve el protocolo NSKeyValueOnserving (KVO), al igual que en otros posts, lo que aquí se explica se puede usar en muchos otros casos.
Este protocolo nos sirve para detectar cambios en los valores de atributos de objetos y funciona de la siguiente manera:
- Tenemos un objeto X del que queremos saber cuando un atributo se modifica
- Tenemos un objeto Y que ha de realizar una acción cuando X modifica su atributo
- El protocolo anterior nos permite que el objeto Y sea avisado de los cambios para el atributo que especifiquemos del objeto X.
Para conseguir esto sólo tenemos que indicarle a X que Y quiere ser avisado de dichos cambios usando el método addObserver:forKeyPath:options:context: definido en el protocolo y realizar las acciones oportunas usando el método observeValueForKeyPath:ofObject:change:context: en el objeto Y, ya que este es el mensaje que se le envía cuando dicho atributo cambia su valor.
Cómo ejemplo usaremos ese método para detectar cuando un usuario selecciona o deselecciona una anotación (MKAnnotation) en un mapa (MKMapView). Primero indicaré como montar el proyecto a modo de tutorial y más abajo como se usa exactamente el KVO, si ya teneis vuestro proyecto montado pasad directamente a la segunda sección.
Preparación
Primero creamos un nuevo proyecto usando la plantilla de Xcode basado en una vista. Añadimos al proyecto el Framework MapKit para poder usar el mapa y las anotaciones.
Luego modificamos el controlador de la vista para añadirle un IBoutlet que nos referencia al mapa que más adelante añadiremos. Tendremos que importar el Framework.
#import <UIKit/UIKit.h>;
#import <MapKit/MapKit.h>;
@interface MapKVOViewController : UIViewController {
IBOutlet MKMapView *theMapView;
}
@end
Abrimos con Interface Builder el archivo MapKVOViewController.xib y le añadimos un MKMapView que asignaremos al IBoutlet creado anteriormente, también indicaremos que el delegado del mapa será el propio controlador.
Para las anotaciones en el mapa necesitamos añadir otra clase a nuestro proyecto, con Xcode añadir una nueva clase a partir de un NSObject, importaremos aquí también el Framework MapKit. Esta clase ha de cumplir con el protocolo MKAnnotation así que modificamos el .h para que así sea.
#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
Cómo esta clase no es más que para un test, inicializaremos las variables a valores constantes, modificar el .m para que se parezca a esto
@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
Para acabar con la preparación del proyecto solo nos falta añadir una anotación en el mapa, por ejemplo usar el viewDidLoad para hacerlo. Acordaros de importar vuestra nueva clase para las anotaciones en el mapa (aquí MapAnnotation).
- (void)viewDidLoad {
[super viewDidLoad];
MapAnnotation *newAnnotation = [[MapAnnotation alloc] init];
[theMapView addAnnotation:newAnnotation];
}
Uso de KVO
En nuestro ejemplo, el objeto X es la vista de la anotación, el pin que se ve en el mapa, es decir, tenemos por un lado la anotación en si y por otro una vista que la representa en el mapa, como la acción de seleccionar se realiza sobre el mapa, es la vista de la anotación, la que tiene un atributo llamado selected, que es el que querremos vigilar.
Por otro lado, el objeto Y, será nuestro MapKVOViewController, que es el encargado de añadir las anotaciones al mapa.
Primer paso, indicarle al objeto X que queremos observar el atributo selected, suponiendo que queramos hacer esto con todas las anotaciones del mapa, un buen sitio es el método mapView:didAddAnnotationViews: del protocolo MKMapViewDelegate, añadir este método a nuestro controlador y modificarlo para que sea como este
- (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];
}
}
Tened en cuenta que esto se hará para cada anotación, por lo que, si tenéis diferentes tipos de anotaciones, tendréis que diferenciar entre cuales queréis observar y cuales no.
El método que nos interesa es addObserver:forKeyPath:options:context:, a éste le indicamos quien será el observador, self, que atributo queremos observar, selected, con que opciones, NSKeyValueObservingOptionNew indica que queremos el nuevo valor, y un contexto, que se usa para darnos mas información, por ejemplo qué estamos observando, aquí podéis usar nil si sólo observáis una cosa, en caso de que tengáis que diferenciar, es muy útil usar constantes para cada caso, aquí usamos
static NSString* const ANNOTATION_SELECTED_DESELECTED = @"mapAnnotationSelectedOrDeselected";
definida al principio de nuestro .m
Finalmente, para poder realizar cualquier acción, una vez notificados del cambio, tenemos que sobrescribir el método 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
}
}
}
Añadir el código para realizar la acción que queráis una vez sepáis si se ha seleccionado o deseleccionado la anotación, por ejemplo reproducir un sonido.
VN:F [1.9.8_1114]
Rating: 5.0/5 (4 votes cast)
VN:F [1.9.8_1114]
Rating: +3 (from 3 votes)