如何自定义MKAnnotationView的标注气泡?

时间:2009-10-14 12:02:27

标签: ios objective-c mapkit mkmapview mkannotationview

我目前正在使用mapkit并且卡住了。

我正在使用自定义注释视图,我想使用image属性在我自己的图标上显示地图上的点。我有这个工作正常。但我还想做的是覆盖默认的标注视图(触摸注释图标时显示标题/副标题的气泡)。我希望能够控制callout本身:mapkit只提供对左右辅助标注视图的访问,但无法为标注气泡提供自定义视图,或者无法为其提供零大小或其他任何内容。< / p>

我的想法是覆盖MKMapViewDelegate中的selectAnnotation / deselectAnnotation,然后通过调用我的自定义注释视图来绘制我自己的自定义视图。这是有效的,但只有在我的自定义注释视图类中将canShowCallout设置为YES时才有效。如果我将此设置为NO(这就是我想要的,那么就不会调用这些方法,因此不会绘制默认的标注气泡)。所以我无法知道用户是否在地图上触摸了我的点(选中它)或触摸了一个不属于我的注释视图(被选中)的点而没有显示默认的标注气泡视图。

我试着走另一条路,只是自己在地图上处理所有触摸事件,我似乎无法让这个工作。我在地图视图中阅读了与捕捉触摸事件相关的其他帖子,但它们并不完全是我想要的。有没有办法挖掘地图视图以在绘制之前删除标注气泡?我很茫然。

有什么建议吗?我错过了一些明显的东西吗?

9 个答案:

答案 0 :(得分:53)

有一个更简单的解决方案。

创建自定义UIView(针对您的标注)。

然后创建MKAnnotationView的子类并覆盖setSelected,如下所示:

- (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...
    }
}
忙碌,完成工作。

答案 1 :(得分:40)

detailCalloutAccessoryView

在过去的日子里,这很痛苦,但Apple解决了这个问题,只需查看MKAnnotationView上的文档

view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "zebra"))

真的,就是这样。采取任何UIView。

答案 2 :(得分:13)

继续@ TappCandy简单明了的答案,如果你想以与默认方式相同的方式为你的泡泡制作动画,我已经制作了这个动画方法:

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

它看起来相当复杂,但只要你的标注气泡设计为中下方,你应该能够用自己的大小替换myBubbleWidthmyBubbleHeight上班。并且请记住确保您的子视图将autoResizeMask属性设置为63(即“全部”),以便它们在动画中正确缩放。

: - 乔

答案 3 :(得分:8)

发现这对我来说是最好的解决方案。 您必须使用一些创造力来进行自定义

MKAnnotationView子类中,您可以使用

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

如果subviewUICalloutView,那么您可以使用它,以及内部的内容。

答案 4 :(得分:6)

我遇到了同样的问题。在此博客http://spitzkoff.com/craig/?p=81上有关于此主题的大量博客文章。

仅使用MKMapViewDelegate对您没有帮助,并且MKMapView的子类化并尝试扩展现有功能对我来说也不起作用。

我最终要做的就是创建我自己的CustomCalloutView,而不是MKMapView。您可以以任何方式设置此视图的样式。

我的CustomCalloutView有一个与此类似的方法:


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

它需要一个MKAnnotation对象并设置自己的标题,然后调用另外两个非常难看的方法来调整标注内容的宽度和大小,然后在正确的位置绘制它周围的语音气泡

最后,视图作为子视图添加到mapView中。此解决方案的问题在于滚动地图视图时很难将标注保持在正确的位置。我只是在地图视图中隐藏了callout,在一个区域更改上委托方法来解决这个问题。

解决所有这些问题需要一些时间,但现在标注的行为几乎与官方的一样,但我有自己的风格。

答案 5 :(得分:5)

基本上要解决这个问题,需要:  a)防止出现默认的标注气泡。  b)找出点击了哪个注释。

我能够通过以下方式实现这些目标: a)将canShowCallout设置为NO b)子类化,MKPinAnnotationView并覆盖touchesBegan和touchesEnd方法。

注意:您需要处理MKAnnotationView的触摸事件而不是MKMapView

答案 6 :(得分:3)

我想出了一个方法,这里的想法是

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

此处 - testview是大小为320x100的UIView - showCallout是BOOL - [self animateIn];是查看UIAlertView之类动画的函数。

答案 7 :(得分:1)

您可以使用leftCalloutView,将annotation.text设置为@“”

请在下面找到示例代码:

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 = @" ";            

答案 8 :(得分:1)

我已经推出了优秀的SMCalloutView的分支,它解决了为标注提供自定义视图并允许灵活宽度/高度的问题。还有一些问题需要解决,但到目前为止它还很有用:

https://github.com/u10int/calloutview