首页 文章

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

提问于
浏览 960
88

我目前正在使用mapkit而且卡住了 .

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

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

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

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

9 回答

  • 53

    detailCalloutAccessoryView

    在过去,这是一个痛苦,但苹果已经解决了它,只需查看MKAnnotationView上的文档

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

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

  • 40

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

    // 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 .

  • 13

    我有同样的问题 . 在这个博客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 对象并设置它自己的 Headers ,然后它调用另外两个非常难看的方法,它们调整标注内容的宽度和大小,然后在正确的位置绘制它周围的语音气泡 .

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

    解决所有这些问题花了一些时间,但现在标注几乎就像官方的那样,但我有自己的风格 .

  • 8

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

    https://github.com/u10int/calloutview

  • 6

    继续@ 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(即"all"),以便它们在动画中正确缩放 .

    :-Joe

  • 5

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

    创建自定义 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...
        }
    }
    

    热潮,完成工作 .

  • 3

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

    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 ,那么你可以用它来解决它内部的问题 .

  • 1

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

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

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

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

相关问题