Subclassificando MKAnnotationView e substituindo setDragState

Isto é sobre um aplicativo para iPhone usando MKMapKit:

Eu criei um MKAnnotationView personalizado para uma anotação arrastável. Eu quero criar uma animação personalizada. Eu defino uma imagem de alfinete e a anotação é arrastável (o que não é mostrado aqui, acontece na visualização do mapa) com o seguinte 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"); } } 

Tudo funciona bem, a anotação é movida um pouco para cima, é arrastável e, quando eu libero a anotação, o mapview recebe a mensagem “dragstateending”.

Mas agora quero que a animação seja executada em um período de tempo e mude o dragStateStarting para o seguinte:

 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]; }]; } 

As animações são executadas conforme desejado durante o período de um segundo e a anotação é arrastável. Mas quando eu libero a anotação, o mapview não está recebendo o final através do delegat. O que eu também reconheci foi que quando eu estou fazendo a animação com “UIView animateWithDuration …” é que imediatamente depois de começar a arrastar, quando a animação começa, o balão da anotação se abre. Quando estou configurando o novo centro sem a animação, o balão fica fechado e só é aberto depois de terminar o arrasto, liberando a anotação.

O que estou fazendo de errado? É este o caminho certo para replace setDragState. Eu realmente tenho que chamar a superclass? Mas sem definir o estado de arrasto na superclass, meu mapa não percebeu as mudanças do estado de arrasto.

Gostaria de saber sobre a implementação original do MKPinAnnotationView, mas porque é uma class interna, não consegui encontrar uma descrição do método setDragState.

Thx por ajuda. Felicidades,

Ben

Eu tive o pin arrastar trabalhando, mas estava tentando descobrir por que as annimations pino que ocorrem quando você não replace setDragState – não funciona mais na minha implementação. Sua pergunta continha minha resposta .. Obrigado!

Parte do problema com o seu código é que, uma vez que você sobrescreve a function setDragState, de acordo com a documentação do xcode, você é responsável por atualizar a variável dragState com base no novo estado. Eu também ficaria um pouco preocupado com seu código chamando ( setDragState chamando [self setDragState]).

Aqui está o código que acabei (com a sua ajuda) que faz todos os levantamentos, arrastes e quedas como eu espero que eles ocorram. Espero que isso ajude voçe tambem!

 - (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; }]; } } 

Enquanto a solução de Brian funcionava, faltava levar em consideração o dedo do usuário bloqueando a visão de anotação que está sendo manipulada.

Isso significa que o usuário não pode posicionar o pino com precisão depois de arrastá-lo. O padrão MKPinAnnotationView faz um ótimo trabalho, o que acontece é quando o dedo começa a arrastar, o pino é levantado acima do dedo e o ponto visual do pino é usado para posicionar não o ponto central anterior, que agora reside sob o dedo.

Além disso, minha implementação também adiciona outra animação ao soltar o pino após arrastá-lo, levantando o pino e soltando-o com uma velocidade maior. Isso está muito próximo da experiência do usuário nativa e será apreciado por seus usuários.

Por favor, confira minha essência no GitHub para o código .

O que é realmente legal é definir um delegado como opcional, opcionalmente, uma notificação é enviada quando a visualização de anotação é colocada de volta no mapa.

Eu não estudava muito o código de Ben, mas não funcionou para mim. Então eu tentei Brian e funciona muito bem. Muito obrigado! Eu tenho tentado resolver a animação de annotations durante o drag’n’drop por um longo tempo.

Mas tenho uma sugestão para a solução de Brian. Eu acho que seria melhor para apoiar delegado do MKMapKit e notificá-lo sobre como alterar dragState e salvar nova posição no método do delegado padrão: - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState . Aqui está meu código:

DraggableAnnotationView.h:

 #import  #import  @interface DraggableAnnotationView : MKAnnotationView { id  delegate; MKAnnotationViewDragState dragState; } @property (nonatomic, assign) id  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