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


88

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

我有一个正在使用的自定义注释视图,我想使用image属性使用自己的图标在地图上显示该点。我的工作很好。但是我还想做的是覆盖默认的标注视图(触摸注释图标时标题/副标题出现的气泡)。我希望能够控制标注本身:mapkit仅提供对左侧和右侧辅助标注视图的访问,但是无法为标注气泡提供自定义视图,也不能为其提供零大小,或其他任何方式。

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

我尝试走另一条路,只是自己在地图中处理所有触摸事件,但似乎无法正常工作。我读了其他与在地图视图中捕获触摸事件有关的帖子,但它们并不是我想要的。有没有办法在绘制之前深入地图视图以删除标注气泡?我很茫然。

有什么建议?我是否缺少明显的东西?


该链接无效,但在此处找到了相同的帖子-> 建立自定义地图注释标注–第1部分
Fede Mika

2
您可以参考该项目进行快速演示。github.com/akshay1188/CustomAnnotation将以上答案汇总为一个正在运行的演示。
akshay1188'9

Answers:


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

景气,工作完成了。


8
嗨,TappCandy!首先,感谢您的解决方案。它确实起作用,但是当加载视图时(我正在从nib文件中进行加载),按钮不起作用。我该怎么做才能使其正常工作?谢谢
Frade 2012年

爱这个解决方案的简单性!
埃里克·布鲁克托

6
嗯-这行不通!它取代了地图注释(即图钉),而不是标注“气泡”。
PapillonUK 2012年

2
这行不通。MKAnnotationView没有对mapview的引用,因此您在添加自定义标注时遇到了一些问题。例如,您不知道是否将其添加为子视图。
卡梅隆·洛厄尔·帕尔默

@PapillonUK您可以控制标注的框架。它不是在替换销,而是在销的顶部。将其添加为子视图时,调整其位置。
本·帕克

40

detailCalloutAccessoryView

在过去,这很痛苦,但是Apple已经解决了,只需检查MKAnnotationView上的文档即可

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

真的,就是这样。接受任何UIView。


最好使用哪一个?
萨蒂扬2014年

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(即“全部”),以便它们在动画中正确缩放。

:-乔


顺便说一句,您可以在scale属性而不是框架上进行动画处理,以便对内容进行适当的缩放。
occulus 2012年

不要使用CGRectMake。使用CGTransform。
卡梅隆·洛厄尔·帕尔默

@occulus-我想我首先尝试过,但是在iOS 4中出现了渲染问题。但这可能是因为我没有使用CABasicAnimation...太早了而无法记住!我知道由于中心偏移,我也必须对y属性进行动画处理。
jowie 2012年

@CameronLowellPalmer-为什么不使用CGRectMake
jowie 2012年

@jowie因为语法更好,并且避免了代码中的魔术值。CGAffineTransformMakeScale(1.1f,1.1f); 使包装盒增长110%是显而易见的。您执行此操作的方式可能会奏效,但效果并不理想。
卡梅隆·洛厄尔·帕尔默

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

如果subviewa是a UICalloutView,则可以拧紧它以及其中的内容。


1
由于UICalloutview不是公共类,如何检查子视图是否为UICalloutView。
Manish Ahuja 2012年

if ([[[subview class] description] isEqualToString:@"UICalloutView"]) 我认为有更好的方法,但是可以。
яοвοτағτєяаււ

您是否对此有任何疑问,因为正如Manish所指出的那样,这是一门私人课。
丹尼尔(Daniel)

可能他们更改了calloutviews的UIView结构。我还没有升级使用该应用程序,让你对自己的:P
яοвοτағτєяаււ

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中。该解决方案的问题在于,滚动地图视图时很难将标注保持在正确的位置。我只是将标注隐藏在区域更改的地图视图委托方法中,以解决此问题。

解决所有这些问题花了一些时间,但现在标注几乎像正式标注一样工作,但我有自己的风格。


我第二个观点是:UICalloutView是一个私有类,在SDK中正式不可用。最好的选择是遵循Sascha的建议。
2009年

您好萨萨,我们感谢您的答复。但是,我们目前遇到的问题是如何在地图上获得显示图钉的位置(x,y,而不是纬度或经度),以便我们可以显示标注视图。
扎克

1
可以通过MKMapView的两个功能轻松地转换坐标:#– convertCoordinate:toPointToView:#– convertPoint:toCoordinateFromView:
Sascha Konietzke,2009年

5

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

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

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


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是一个UIView尺寸为320x100的图片-showCallout是BOOL-是[self animateIn];用来查看动画的函数UIAlertView


selectedCoordinate指定了什么?那是什么类型的对象?
Pradeep Reddy Kypa '10 -10-9

它是CLLocationCoordinate2d对象。
bharathi kumar

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

1
这将在某种程度上“起作用”,但是并不能真正回答所提出的更普遍的问题,而且它非常hackack。
卡梅隆·洛厄尔·帕尔默

1

我已经推出了出色的SMCalloutView,这可以通过为标注提供自定义视图并轻松实现灵活的宽度/高度来解决此问题。仍有一些怪癖需要解决,但到目前为止它的功能还很不错:

https://github.com/u10int/calloutview

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.