2010-09-09 17 views
9

Se trata de una aplicación de iPhone usando MKMapKit:subclases MKAnnotationView y setDragState primordial

yo creamos una costumbre MKAnnotationView para una anotación que pueden arrastrarse. Quiero crear una animación personalizada. Me puse una imagen de pin costumbre y la anotación es arrastrable (que ambos no se muestra aquí, sucede en el MapView) con el siguiente código:

- (void) movePinUpFinished { 

    [super setDragState:MKAnnotationViewDragStateDragging]; 
    [self setDragState:MKAnnotationViewDragStateDragging]; 
} 

- (void) setDragState:(MKAnnotationViewDragState) myState { 
    if (myState == MKAnnotationViewDragStateStarting) { 
      NSLog(@"starting"); 
      CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20); 
      self.center = endPoint; 
      [self movePinUpFinished]; 
    } 
    if (myState == MKAnnotationViewDragStateEnding) { 
      NSLog(@"ending"); 
      [super setDragState:MKAnnotationViewDragStateEnding]; 
      [self setDragState:MKAnnotationViewDragStateNone]; 
      [super setDragState:MKAnnotationViewDragStateNone]; 
    } 
    if (myState == MKAnnotationViewDragStateDragging) { 
      NSLog(@"dragging"); 
    } 
    if (myState == MKAnnotationViewDragStateCanceling) { 
      NSLog(@"cancel"); 
    } 
    if (myState == MKAnnotationViewDragStateNone) { 
      NSLog(@"none"); 
    } 
} 

Todo funciona bien, la anotación se mueve un poco, es arrastrable y cuando lanzo la anotación, la vista de mapa recibe el "estado de arrastre".

pero ahora quiero que la animación se ejecuta durante un período de tiempo y cambiar el dragStateStarting a lo siguiente:

if (myState == MKAnnotationViewDragStateStarting) { 
      NSLog(@"starting"); 
      CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20); 
      [UIView animateWithDuration:1.0 
      animations:^{ self.center = endPoint; } 
      completion:^(BOOL finished){ [self movePinUpFinished]; }]; 
    } 

Las animaciones se ejecuta como quería en el período de un segundo y la anotación es arrastrable. Pero cuando publico la anotación, la vista de mapa no está recibiendo la terminación a través del delegat. Lo que también reconocí fue que cuando estoy haciendo la animación con "UIView animateWithDuration ..." es que inmediatamente después de comenzar el arrastre, cuando comienza la animación, se abre el globo de la anotación. Cuando estoy configurando el nuevo centro sin la animación, el globo se mantiene cerrado y solo se abre después de terminar el arrastre soltando la anotación.

¿Qué estoy haciendo mal? ¿Es esta la manera correcta de anular setDragState? ¿Realmente tengo que llamar a la súper clase? Pero sin establecer el estado de arrastre en la superclase, mi vista de mapa no se dio cuenta de los cambios del estado de arrastre.

Me pregunto acerca de la implementación original de MKPinAnnotationView, pero como es una clase interna I no pude encontrar una descripción del método setDragState.

Thx para obtener ayuda. Saludos,

Ben

Respuesta

23

yo tuvimos el arrastre pasador de trabajo, pero estaba tratando de averiguar por qué los annimations pin que se producen al no anular setDragState - ya no funcionan en mi aplicación. Tu pregunta contenía mi respuesta ... ¡Gracias!

Parte del problema con su código es que una vez que sobrescribe la función setDragState, según la documentación de xcode, usted es responsable de actualizar la variable dragState en función del nuevo estado entrante. También me preocupa un poco su código que se llama a sí mismo (setDragState calling [self setDragState]).

Aquí está el código que terminé (con su ayuda) que hace todos los levantamientos, arrastra y cae como espero que ocurran. ¡Espero que esto le ayude también!

- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated 
{ 
    if (newDragState == MKAnnotationViewDragStateStarting) 
    { 
     // lift the pin and set the state to dragging 

     CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20); 
     [UIView animateWithDuration:0.2 
         animations:^{ self.center = endPoint; } 
         completion:^(BOOL finished) 
          { self.dragState = MKAnnotationViewDragStateDragging; }]; 
    } 
    else if (newDragState == MKAnnotationViewDragStateEnding) 
    { 
     // save the new location, drop the pin, and set state to none 

     /* my app specific code to save the new position 
     objectObservations[ACTIVE].latitude = pinAnnotation.coordinate.latitude; 
     objectObservations[ACTIVE].longitude = pinAnnotation.coordinate.longitude; 
     posChanged = TRUE; 
     */ 

     CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20); 
     [UIView animateWithDuration:0.2 
         animations:^{ self.center = endPoint; } 
         completion:^(BOOL finished) 
          { self.dragState = MKAnnotationViewDragStateNone; }]; 
    } 
    else if (newDragState == MKAnnotationViewDragStateCanceling) 
    { 
     // drop the pin and set the state to none 

     CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20); 
     [UIView animateWithDuration:0.2 
         animations:^{ self.center = endPoint; } 
         completion:^(BOOL finished) 
          { self.dragState = MKAnnotationViewDragStateNone; }]; 
    } 
} 
+0

¿Cómo funcionó perfectamente? Cuando arrastra el pin fuera de la pantalla, se produce un bloqueo. – akshay1188

+0

Esto funciona, pero una cosa me desconcierta: en mi MKMapViewDelegate, sobreescribo mapView: annotationView: didChangeDragState. La clase base MKAnnotationView produce dos transiciones: desde 0-> 1 cuando se inicia el arrastre, y 1-> 4 cuando termina. Esta solución anterior parece seguir las directrices de Apple para la T, pero produce una transición de 0-> 2 y una 2-> 0. Puedo vivir con eso, pero es extraño, creo que la documentación de Apple está apagada, para ser honesto. – n13

+1

Creo que necesitas llamar 'self.dragState = ...', no 'dragState ='. De lo contrario, las notificaciones KVO no se enviarán, y la vista del mapa no verá el cambio del estado de arrastre. – Rick

2

No estudié mucho el código de Ben pero no funcionó para mí. Así que probé con Brian y funciona muy bien. ¡Muchas gracias! He intentado resolver la animación de la anotación durante arrastrar y soltar durante mucho tiempo.

Pero tengo una sugerencia para la solución de Brian. Creo que sería mejor apoyar al delegado de MKMapKit y notificarlo sobre el cambio de dragState y guardar una nueva posición dentro del método de delegado estándar: - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState. Aquí está mi código:

DraggableAnnotationView.h:

#import <Foundation/Foundation.h> 
#import <MapKit/MapKit.h> 

@interface DraggableAnnotationView : MKAnnotationView 
{ 
    id <MKMapViewDelegate> delegate; 
    MKAnnotationViewDragState dragState; 
} 

@property (nonatomic, assign) id <MKMapViewDelegate> delegate; 
@property (nonatomic, assign) MKAnnotationViewDragState dragState; 
@property (nonatomic, assign) MKMapView *mapView; 

@end 

DraggableAnnotationView.m:

#import "DraggableAnnotationView.h" 
#import "MapAnnotation.h" 

@implementation DraggableAnnotationView 
@synthesize delegate, dragState, mapView; 



- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated 
{ 
    [delegate mapView:mapView annotationView:self didChangeDragState:newDragState fromOldState:dragState]; 

    if (newDragState == MKAnnotationViewDragStateStarting) { 
     // lift the pin and set the state to dragging 
     CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20); 
     [UIView animateWithDuration:0.2 
         animations:^{ self.center = endPoint; } 
         completion:^(BOOL finished) 
     { dragState = MKAnnotationViewDragStateDragging; }]; 
    } else if (newDragState == MKAnnotationViewDragStateEnding) { 
     // drop the pin, and set state to none 

     CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20); 
     [UIView animateWithDuration:0.2 
         animations:^{ self.center = endPoint; } 
         completion:^(BOOL finished) 
     { dragState = MKAnnotationViewDragStateNone; }]; 
    } else if (newDragState == MKAnnotationViewDragStateCanceling) { 
     // drop the pin and set the state to none 

     CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20); 
     [UIView animateWithDuration:0.2 
         animations:^{ self.center = endPoint; } 
         completion:^(BOOL finished) 
     { dragState = MKAnnotationViewDragStateNone; }]; 
    } 
} 

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

@end 
+0

Se bloquea cuando arrastramos el pin a los bordes de la vista, y nuestro dedo se desliza fuera de la pantalla. – akshay1188

3

Mientras Brian's solution trabajadas, que carecía teniendo en cuenta el dedo a los usuarios bloquear la vista de anotación que está siendo manipulado.

Esto significa que el usuario no pudo colocar el pin con precisión una vez que lo estaba arrastrando. El estándar MKPinAnnotationView hace un gran trabajo en esto, lo que sucede es cuando el dedo comienza a arrastrarse, el pasador se levanta por encima del dedo y el punto visual del pasador se usa para colocarlo, no en el punto central anterior que ahora reside debajo del dedo.

Además de esto, mi implementación también agrega otra animación al soltar el pin después de arrastrar, levantando el pin y soltándolo a una velocidad mayor. Esta es una experiencia de usuario nativa muy cercana y será apreciada por sus usuarios.

Por favor revisa mi gist on GitHub for the code.

Lo que es realmente genial es configurar un delegado es opcional, opcionalmente se envía una notificación cuando la vista de anotación se vuelve a colocar en el mapa.

+1

Esta implementación está mucho más cerca del comportamiento de Apple que las otras respuestas. ¡Genial, gracias! –

+0

Existen algunos errores de ortografía en los nombres de #import y @interface, pero son muy fáciles de corregir. Buen trabajo. ¡Gracias! – demosten

+0

Bueno, eso es vergonzoso, lo arregló ahora. Gracias por el encabezado – Daniel

Cuestiones relacionadas