ios - example - location manager swift tutorial
¿Cómo personalizar la burbuja de leyenda para MKAnnotationView? (9)
detailCalloutAccessoryView
En los viejos tiempos esto era un problema, pero Apple lo ha resuelto, solo revisa los documentos en MKAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "zebra"))
Realmente, eso es todo. Toma cualquier UIView.
Actualmente estoy trabajando con el kit de mapas y estoy atascado.
Tengo una vista de anotación personalizada que estoy usando, y quiero usar la propiedad de la imagen para mostrar el punto en el mapa con mi propio ícono. Tengo esto funcionando bien. Pero lo que también me gustaría hacer es anular la vista de llamada predeterminada (la burbuja que aparece con el título / subtítulo cuando se toca el icono de la anotación). Deseo poder controlar la llamada en sí: el mapa solo proporciona acceso a las vistas de leyenda auxiliar izquierda y derecha, pero no es posible proporcionar una vista personalizada para la burbuja de leyenda, ni darle tamaño cero, ni ninguna otra cosa.
Mi idea era anular selectAnnotation / deselectAnnotation en mi MKMapViewDelegate
, y luego dibujar mi propia vista personalizada haciendo una llamada a mi vista de anotación personalizada. Esto funciona, pero solo cuando canShowCallout
está establecido en YES
en mi clase de vista de anotación personalizada. Estos métodos NO se invocan si configuro NO
(que es lo que quiero, de modo que no se dibuja la burbuja de llamada predeterminada). Por lo tanto, no tengo forma de saber si el usuario tocó mi punto en el mapa (lo seleccionó) o tocó un punto que no es parte de mis vistas de anotación (lo eliminó) sin que aparezca la vista de burbuja de llamada predeterminada.
Intenté seguir un camino diferente y simplemente manejar todos los eventos táctiles en el mapa, y parece que no puedo hacer que esto funcione. He leído otras publicaciones relacionadas con la captura de eventos táctiles en la vista del mapa, pero no son exactamente lo que quiero. ¿Hay alguna manera de profundizar en la vista de mapa para eliminar la burbuja de llamada antes de dibujar? Estoy perdido
¿Alguna sugerencia? ¿Me estoy perdiendo algo obvio?
Acabo de presentar un enfoque, la idea aquí es
// Detect the touch point of the AnnotationView ( i mean the red or green pin )
// Based on that draw a UIView and add it to subview.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
// NSLog(@"regionWillChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
[testview setCenter:CGPointMake(newPoint.x+5,newPoint.y-((testview.frame.size.height/2)+35))];
[testview setHidden:YES];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
// NSLog(@"regionDidChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
[testview setCenter:CGPointMake(newPoint.x,newPoint.y-((testview.frame.size.height/2)+35))];
[testview setHidden:NO];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"Select");
showCallout = YES;
CGPoint point = [self.mapView convertPoint:view.frame.origin fromView:view.superview];
[testview setHidden:NO];
[testview setCenter:CGPointMake(point.x+5,point.y-(testview.frame.size.height/2))];
selectedCoordinate = view.annotation.coordinate;
[self animateIn];
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"deSelect");
if(!showCallout)
{
[testview setHidden:YES];
}
}
Aquí - testview es un UIView
de tamaño 320x100 - showCallout es BOOL - [self animateIn];
es la función que sí ve la animación como UIAlertView
.
Básicamente para resolver esto, se necesita: a) Evitar que aparezca la burbuja de leyenda predeterminada. b) Determine en qué anotación se hizo clic.
Pude lograr esto: a) estableciendo canShowCallout en NO b) subclassing, MKPinAnnotationView y anulando los métodos touchesBegan y touches End.
Nota: Necesita manejar los eventos táctiles para MKAnnotationView y no para MKMapView
Continuando con la respuesta brillante de @TappCandy, si quieres animar tu burbuja de la misma manera que la predeterminada, he producido este método de animación:
- (void)animateIn
{
float myBubbleWidth = 247;
float myBubbleHeight = 59;
calloutView.frame = CGRectMake(-myBubbleWidth*0.005+8, -myBubbleHeight*0.01-2, myBubbleWidth*0.01, myBubbleHeight*0.01);
[self addSubview:calloutView];
[UIView animateWithDuration:0.12 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^(void) {
calloutView.frame = CGRectMake(-myBubbleWidth*0.55+8, -myBubbleHeight*1.1-2, myBubbleWidth*1.1, myBubbleHeight*1.1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 animations:^(void) {
calloutView.frame = CGRectMake(-myBubbleWidth*0.475+8, -myBubbleHeight*0.95-2, myBubbleWidth*0.95, myBubbleHeight*0.95);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.075 animations:^(void) {
calloutView.frame = CGRectMake(-round(myBubbleWidth/2-8), -myBubbleHeight-2, myBubbleWidth, myBubbleHeight);
}];
}];
}];
}
Parece bastante complicado, pero siempre que el punto de su burbuja esté diseñado para ser el centro de la cuestión, debería poder reemplazar myBubbleWidth
y myBubbleHeight
con su propio tamaño para que funcione. Y recuerde asegurarse de que sus subvistas tengan su propiedad autoResizeMask
establecida en 63 (es decir, "todas") para que se escalen correctamente en la animación.
: -Joe
Encontré que esta es la mejor solución para mí. Tendrás que usar algo de creatividad para hacer tus propias personalizaciones
En su subclase MKAnnotationView
, puede usar
- (void)didAddSubview:(UIView *)subview{
int image = 0;
int labelcount = 0;
if ([[[subview class] description] isEqualToString:@"UICalloutView"]) {
for (UIView *subsubView in subview.subviews) {
if ([subsubView class] == [UIImageView class]) {
UIImageView *imageView = ((UIImageView *)subsubView);
switch (image) {
case 0:
[imageView setImage:[UIImage imageNamed:@"map_left"]];
break;
case 1:
[imageView setImage:[UIImage imageNamed:@"map_right"]];
break;
case 3:
[imageView setImage:[UIImage imageNamed:@"map_arrow"]];
break;
default:
[imageView setImage:[UIImage imageNamed:@"map_mid"]];
break;
}
image++;
}else if ([subsubView class] == [UILabel class]) {
UILabel *labelView = ((UILabel *)subsubView);
switch (labelcount) {
case 0:
labelView.textColor = [UIColor blackColor];
break;
case 1:
labelView.textColor = [UIColor lightGrayColor];
break;
default:
break;
}
labelView.shadowOffset = CGSizeMake(0, 0);
[labelView sizeToFit];
labelcount++;
}
}
}
}
Y si la subview
es una UICalloutView
, entonces puedes UICalloutView
y lo que hay dentro.
Hay una solución aún más fácil.
Cree una UIView
personalizada (para su texto destacado).
A continuación, cree una subclase de MKAnnotationView
y anule setSelected
siguiente manera:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
//Add your custom view to self...
}
else
{
//Remove your custom view...
}
}
Boom, trabajo hecho.
He sacado el tenedor de la excelente SMCalloutView que resuelve el problema al proporcionar una vista personalizada para las leyendas y permitir anchos / alturas flexibles de forma sencilla y sin problemas. Todavía hay algunos caprichos para resolver, pero hasta ahora es bastante funcional:
Puede usar leftCalloutView, estableciendo annotation.text en @ ""
A continuación, encontrará el código de ejemplo:
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if(pinView == nil){
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
}
CGSize sizeText = [annotation.title sizeWithFont:[UIFont fontWithName:@"HelveticaNeue" size:12] constrainedToSize:CGSizeMake(150, CGRectGetHeight(pinView.frame)) lineBreakMode:UILineBreakModeTailTruncation];
pinView.canShowCallout = YES;
UILabel *lblTitolo = [[UILabel alloc] initWithFrame:CGRectMake(2,2,150,sizeText.height)];
lblTitolo.text = [NSString stringWithString:ann.title];
lblTitolo.font = [UIFont fontWithName:@"HelveticaNeue" size:12];
lblTitolo.lineBreakMode = UILineBreakModeTailTruncation;
lblTitolo.numberOfLines = 0;
pinView.leftCalloutAccessoryView = lblTitolo;
[lblTitolo release];
annotation.title = @" ";
Yo tuve el mismo problema. Hay una serie de publicaciones de blogs sobre este tema en este blog http://spitzkoff.com/craig/?p=81 .
Simplemente el uso de MKMapViewDelegate
no lo ayuda aquí y la subclasificación de MKMapView
y el intento de extender la funcionalidad existente tampoco funcionó para mí.
Lo que terminé haciendo es crear mi propia CustomCalloutView
que tengo en la parte superior de mi MKMapView
. Puede diseñar esta vista de la forma que desee.
Mi CustomCalloutView
tiene un método similar a este:
- (void) openForAnnotation: (id)anAnnotation
{
self.annotation = anAnnotation;
// remove from view
[self removeFromSuperview];
titleLabel.text = self.annotation.title;
[self updateSubviews];
[self updateSpeechBubble];
[self.mapView addSubview: self];
}
Toma un objeto MKAnnotation
y establece su propio título, luego llama a otros dos métodos que son bastante feos, que ajustan el ancho y el tamaño del contenido del texto destacado y luego dibujan el MKAnnotation
lo rodea en la posición correcta.
Finalmente, la vista se agrega como una subvista al mapView. El problema con esta solución es que es difícil mantener la llamada en la posición correcta cuando se desplaza la vista del mapa. Solo estoy ocultando la leyenda en el método delegado de vistas de mapa en un cambio de región para resolver este problema.
Me llevó algo de tiempo resolver todos esos problemas, pero ahora el rótulo casi se comporta como el oficial, pero lo tengo en mi propio estilo.