无需使用标注附属视图即可在标注弹出窗口任意位置轻触

19

我有一个地图视图,类似于以下方式添加注释:

- (MKAnnotationView *)mapView:(MKMapView *)mapView
            viewForAnnotation:(id <MKAnnotation>) annotation
{
    MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                                       reuseIdentifier:@"MKPinAnnotationView"];
    annotationView.canShowCallout = YES;

    UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    [detailButton addTarget:self
                     action:@selector(handleButtonAction)
           forControlEvents:UIControlEventTouchUpInside];
    annotationView.rightCalloutAccessoryView = detailButton;

    return annotationView;
}
在iOS 7中,这会在呼出框右侧放置一个“i”图标。点击图标会触发委托的mapView:annotationView:calloutAccessoryControlTapped:和自身的handleButtonAction:方法。然而,我最近意识到,您也可以在呼出框的任何其他位置进行点击,这两种方法也会被激活。这种情况在使用UIButtonTypeDetailDisclosure类型的按钮时会发生,但是在使用UIButtonTypeCustom类型的按钮时似乎不会发生。当没有附件视图时,单击呼出框时也不会触发委托方法。(当然,这种行为并不令人惊讶;令人惊讶的是,如果附件视图是详细信息披露按钮,则无论您单击按钮本身还是仅在呼出框的其他地方单击,这两种方法都会触发。)

我想要摆脱呼出框中的按钮——或者至少用显示我自己图像的按钮替换它,而仍然允许用户在呼出框的任何位置进行点击以触发我的操作。这是否可能?我没有看到对应于“呼出框被单击”的MKMapViewDelegate方法。


1
你可以为自己的按钮添加自定义的IBAction,并忽略默认的MKMapViewDelegate。 - Lefteris
@Lefteris,我不明白你的意思。我通过编程构建我的UI,所以我没有IBActions;但是你的意思是我可以在按钮上设置自己的事件处理程序吗?这就是我在代码示例中所做的。问题是我想捕获所有在标注上的点击,而不仅仅是在我的按钮上的点击。 - bdesham
5个回答

28
尝试在不更改UIButtonTypeDetailDisclosure类型的情况下设置自定义按钮图像。
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];        
[detailButton setImage:[UIImage imageNamed:@"icon"] forState:UIControlStateNormal];

对于 iOS 7 及以上版本,该图标默认会被着色。如果您想保留原始图标,请使用以下方法

[[UIImage imageNamed:@"icon"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]

或者如果你想完全移除图标

[detailButton setImage:[UIImage new] forState:UIControlStateNormal];

最后一个建议,使用 [UIImage new],虽然能正常工作,但外观不佳——标注包括一个大的空白区域,在那里按钮会出现。(将按钮的边界设置为 CGRectZero 也没有帮助。)使用我的自定义图像就可以了。谢谢! - bdesham
1
好的答案!在iOS 9上运行良好。 - Yariv Nissim
1
这是唯一帮助了我的注释 :) 谢谢 - Mudriy Ilya

9

在用户点击注释视图后,要点击呼出按钮,请在didSelectAnnotationView中添加一个UITapGestureRecognizer。这样,您就可以实现对呼叫视图的单击,而无需使用附件视图。

然后,您可以从发送方获取注释对象以进行进一步操作。

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(calloutTapped:)];
    [view addGestureRecognizer:tapGesture];
}

-(void)calloutTapped:(UITapGestureRecognizer *) sender
{
    NSLog(@"Callout was tapped");

    MKAnnotationView *view = (MKAnnotationView*)sender.view;
    id <MKAnnotation> annotation = [view annotation];
    if ([annotation isKindOfClass:[MKPointAnnotation class]])
    {
        [self performSegueWithIdentifier:@"annotationDetailSegue" sender:annotation];
    }
}

1
很棒的解决方案。我将它移植到Swift上,它完美地工作了。谢谢! - Claus
最好在创建MKAnnotationView时就添加tapGestureRecognizer,这样每次点击注释视图时都会向视图添加新的手势识别器,即使它已经有了手势识别器,也不会出现问题。 - Marcus
你从不移除手势识别器,这让我觉得这里可能存在问题。 - pronebird

8

Dhanu A在Swift 3中的解决方案:

func mapView(mapView: MKMapView, didSelectAnnotationView view:MKAnnotationView) {
    let tapGesture = UITapGestureRecognizer(target:self,  action:#selector(calloutTapped(sender:)))
    view.addGestureRecognizer(tapGesture)
}

func mapView(mapView: MKMapView, didDeselectAnnotationView view: MKAnnotationView) {
    view.removeGestureRecognizer(view.gestureRecognizers!.first!)
}

func calloutTapped(sender:UITapGestureRecognizer) {
    let view = sender.view as! MKAnnotationView
    if let annotation = view.annotation as? MKPointAnnotation {
        performSegue(withIdentifier: "annotationDetailSegue", sender: annotation)
    }
}

到目前为止最好的答案!您还可以维护一个附加手势识别器的数组,以避免在有很多手势识别器的情况下删除随机手势识别器。 - pronebird

3
如果您不需要点击指示,我建议在创建标注时添加UITapGestureRecognizer并将处理对象(可能是控制器)作为目标,并具有适当的操作方式。

我肯定更喜欢有水龙头指示,但还是谢谢你的建议! - bdesham

0
- (void)mapView:(MKMapView *)mv annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
    Park *parkAnnotation = (Park *)[view annotation];
    switch ([control tag]) {
        case 0: // left button
        {
            NSURL *url = [NSURL URLWithString:parkAnnotation.link];
            [[UIApplication sharedApplication] openURL:url];
        }
            break;

        case 1: // right button
        {
            // build a maps url. This will launch the Maps app on the hardware, and the apple maps website in the simulator
            CLLocationCoordinate2D coordinate = self.locationManager.location.coordinate;
            NSString *url2 = [NSString stringWithFormat:@"http://maps.apple.com/maps?saddr=%f,%f&daddr=%f,%f",coordinate.latitude,coordinate.longitude,parkAnnotation.location.coordinate.latitude,parkAnnotation.location.coordinate.longitude];

            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url2]];
        }
            break;

        default:
            NSLog(@"Should not be here in calloutAccessoryControlTapped, tag=%ld!",(long)[control tag]);
            break;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接