2013-07-21 36 views
22

我正在开发应用程序,其中用户通过gps进行本地化,然后询问他是否位于特定位置。为了证实这一点,标注泡沫马上向他呈现,询问他是否在特定的地方。自定义MKAnnotation标注按钮

由于有很多类似的问题,我能够做的定制标注泡沫: Custom callout bubble

我的问题:按钮是“无法点击” 我的猜测:因为这个自定义标注比标准标注泡沫较高,我不得不把它放在负面的“框架”,因此按钮不能点击。这是我didSelectAnnotationView方法

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { 
    if(![view.annotation isKindOfClass:[MKUserLocation class]]) { 
     CalloutView *calloutView = (CalloutView *)[[[NSBundle mainBundle] loadNibNamed:@"callOutView" owner:self options:nil] objectAtIndex:0]; 
     CGRect calloutViewFrame = calloutView.frame; 
     calloutViewFrame.origin = CGPointMake(-calloutViewFrame.size.width/2 + 15, -calloutViewFrame.size.height); 
     calloutView.frame = calloutViewFrame; 
     [calloutView.calloutLabel setText:[(MyLocation*)[view annotation] title]]; 
     [calloutView.btnYes addTarget:self 
           action:@selector(checkin) 
        forControlEvents:UIControlEventTouchUpInside]; 
     calloutView.userInteractionEnabled = YES; 
     view.userInteractionEnabled = YES; 
     [view addSubview:calloutView]; 
    } 

} 

CalloutView只是简单类2点的属性(标签显示的位置和按钮名称),并用XIB1。

我一直在做这个自定义标注泡沫几天。我尝试使用“异步解决方案”solution,但我无法添加任何其他类型的按钮,然后泄露按钮。

我的下一个尝试是找到比异步解决方案更容易的东西,并将其修改为我的用法。那就是我如何找到tochi's custom callout

Tochi's custom callout

根据他的工作,我可以自定义他泡和更改信息按钮为我的自定义按钮。然而,我的问题仍然是一样的。为了将我的自定义标注视图放在引脚上,我必须给它一个负帧,所以我的按钮只能在最底部5个像素点“可点击”。看来,我必须深入研究ios默认标注泡泡,对其进行子类化并在其中更改标注框架。但我现在真的无望了。

如果你们能向我展示正确的方式,或给我建议,我会很高兴。

回答

75

有几种方法来定制标注:

  1. 最简单的方法是使用现有的右侧和左侧标注配件,并把你的按钮,在其中的一个。例如:

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { 
        static NSString *identifier = @"MyAnnotationView"; 
    
        if ([annotation isKindOfClass:[MKUserLocation class]]) { 
         return nil; 
        } 
    
        MKPinAnnotationView *view = (id)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; 
        if (view) { 
         view.annotation = annotation; 
        } else { 
         view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier]; 
         view.canShowCallout = true; 
         view.animatesDrop = true; 
         view.rightCalloutAccessoryView = [self yesButton]; 
        } 
    
        return view; 
    } 
    
    - (UIButton *)yesButton { 
        UIImage *image = [self yesButtonImage]; 
    
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 
        button.frame = CGRectMake(0, 0, image.size.width, image.size.height); // don't use auto layout 
        [button setImage:image forState:UIControlStateNormal]; 
        [button addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventPrimaryActionTriggered]; 
    
        return button; 
    } 
    
    - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { 
        NSLog(@"%s", __PRETTY_FUNCTION__); 
    } 
    

    国债收益率:

    enter image description here

  2. 如果你真的不喜欢的权利,其中配件一般去的按钮,可以关闭该附件,而iOS 9提供了指定detailCalloutAccessoryView的机会,它可以用任何您想要的视图替换标注的小标题:

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { 
        static NSString *identifier = @"MyAnnotationView"; 
    
        if ([annotation isKindOfClass:[MKUserLocation class]]) { 
         return nil; 
        } 
    
        MKPinAnnotationView *view = (id)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; 
        if (view) { 
         view.annotation = annotation; 
        } else { 
         view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier]; 
         view.canShowCallout = true; 
         view.animatesDrop = true; 
        } 
        view.detailCalloutAccessoryView = [self detailViewForAnnotation:annotation]; 
    
        return view; 
    } 
    
    - (UIView *)detailViewForAnnotation:(PlacemarkAnnotation *)annotation { 
        UIView *view = [[UIView alloc] init]; 
        view.translatesAutoresizingMaskIntoConstraints = false; 
    
        UILabel *label = [[UILabel alloc] init]; 
        label.text = annotation.placemark.name; 
        label.font = [UIFont systemFontOfSize:20]; 
        label.translatesAutoresizingMaskIntoConstraints = false; 
        label.numberOfLines = 0; 
        [view addSubview:label]; 
    
        UIButton *button = [self yesButton]; 
        [view addSubview:button]; 
    
        NSDictionary *views = NSDictionaryOfVariableBindings(label, button); 
    
        [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[label]|" options:0 metrics:nil views:views]]; 
        [view addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 
        [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]-[button]|" options:0 metrics:nil views:views]]; 
    
        return view; 
    } 
    
    - (UIButton *)yesButton { 
        UIImage *image = [self yesButtonImage]; 
    
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 
        button.translatesAutoresizingMaskIntoConstraints = false; // use auto layout in this case 
        [button setImage:image forState:UIControlStateNormal]; 
        [button addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventPrimaryActionTriggered]; 
    
        return button; 
    } 
    

    只这产生了:

    enter image description here

  3. 如果你真的想开发一个定制标注自己,Location and Maps Programming Guide概述有关步骤:

    在iOS应用程序,它使用mapView:annotationView:calloutAccessoryControlTapped:好的做法委托方法在用户点击标注视图的控件时进行响应(只要控件是UIControl的后代)。在实现此方法时,您可以发现标注视图标注视图的标识,以便您知道用户点击了哪个标注。在Mac应用程序中,标注视图的视图控制器可以实现一个操作方法,该方法在用户在标注视图中单击控件时进行响应。

    当您使用自定义视图而不是标准调出时,您需要做额外的工作以确保您的调出显示并在用户与其进行交互时正确隐藏。以下步骤概述创建包含按钮,自定义标注的过程:

    • 设计的NSViewUIView子类,表示自定义标注。子类可能需要实现drawRect:方法来绘制自定义内容。

    • 创建一个视图控制器,用于初始化标注视图并执行与该按钮相关的操作。

    • 在注释视图中,实现hitTest:以响应位于注释视图边界之外但位于标注视图边界内的匹配,如清单6-7所示。

    • 在注释视图中,实现setSelected:animated:以在用户单击或点击时将标注视图添加为注释视图的子视图。

    • 如果标注视图在用户选择时已经可见,则setSelected:方法应从注释视图中删除标注子视图(请参见清单6-8)。

    • 在注释视图的initWithAnnotation:方法中,将canShowCallout属性设置为NO,以防止地图在用户选择注释时显示标准标注。

    虽然这是斯威夫特,https://github.com/robertmryan/CustomMapViewAnnotationCalloutSwift说明的你如何能做到标注的这个完全定制的例子(如改变标注泡泡的形状,改变背景颜色等)。前一点概述了一个相当复杂的场景(即,您必须编写自己的代码来检测视图外部的水龙头以便将其解除)。如果你支持的iOS 9,你可能只是用,例如:

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { 
        static NSString *identifier = @"MyAnnotationView"; 
    
        if ([annotation isKindOfClass:[MKUserLocation class]]) { 
         return nil; 
        } 
    
        MKPinAnnotationView *view = (id)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; 
        if (view) { 
         view.annotation = annotation; 
        } else { 
         view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier]; 
         view.canShowCallout = false; // note, we're not going to use the system callout 
         view.animatesDrop = true; 
        } 
    
        return view; 
    } 
    
    - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { 
        PopoverController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"AnnotationPopover"]; 
        controller.modalPresentationStyle = UIModalPresentationPopover; 
    
        controller.popoverPresentationController.sourceView = view; 
    
        // adjust sourceRect so it's centered over the annotation 
    
        CGRect sourceRect = CGRectZero; 
        sourceRect.origin.x += [mapView convertCoordinate:view.annotation.coordinate toPointToView:mapView].x - view.frame.origin.x; 
        sourceRect.size.height = view.frame.size.height; 
        controller.popoverPresentationController.sourceRect = sourceRect; 
    
        controller.annotation = view.annotation; 
    
        [self presentViewController:controller animated:TRUE completion:nil]; 
    
        [mapView deselectAnnotation:view.annotation animated:true]; // deselect the annotation so that when we dismiss the popover, the annotation won't still be selected 
    } 
    
+0

罗布太感谢你了,你是真正的救星。我很高兴能够接受并接受你的回答:) – Yanchi

+1

如果我没有在“CustomAnnotation”类中设置标题和副标题,则根本不会调用标注。如何进行? – Satyam

+0

@Satyamsvv如果你将'canShowCallout'设置为'NO',那么对'didSelectAnnotationView'的调用将不会受'title' /'subtitle'的存在或缺失的影响。 – Rob

1

彼时我采取了不同的方法一酥料饼的视图控制器。我尝试了其他的,但他们似乎臃肿,我不想增加更多的类或依靠MKMapViewDelegate处理交互。

我改写了setSelected:我的MKAnnotationView子类的动画。诀窍是在选中它之后展开annotationView的边界,以完全包含调出视图,然后在取消选择后将它们恢复为正常。这将允许您的自定义调用接受超出MKAnnotationView原始界限的触摸和交互。

这里是一个下调的代码示例让任何人开始:

#define kAnnotationCalloutBoxTag 787801 
#define kAnnotationCalloutArrowTag 787802 
#define kAnnotationTempImageViewTag 787803 

-(void)setSelected:(BOOL)selected animated:(BOOL)animated 
{ 
    if (selected == self.selected) 
    { 
     NSLog(@"annotation already selected, abort!"); 
     return; 
    } 
    if (selected) 
    { 
     self.image = nil; //hide default image otherwise it takes the shape of the entire bounds 

     UIView* calloutBox = [self newCustomCallout]; 
     float imgW = [self unselectedSize].width; 
     float imgH = [self unselectedSize].height; 
     float arrowW = 20; 
     float arrowH = 12; 

     //Annotation frames wrap a center coordinate, in this instance we want the call out box to fall under the 
     //central coordinate, so we need to adjust the height to be double what the callout box height would be 
     //making the height *2, this is to make sure the callout view is inside of it. 
     self.bounds = CGRectMake(0, 0, calloutBox.frame.size.width, calloutBox.frame.size.height*2 + arrowH*2 + imgH); 
     CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); 

     UIView* imgView = [[UIImageView alloc] initWithImage:icon]; 
     [imgView setFrame:CGRectMake(center.x - imgW/2, center.y-imgH/2, imgW, imgH)]; 
     imgView.tag = kAnnotationTempImageViewTag; 
     [self addSubview:imgView]; 

     UIView* triangle = [self newTriangleViewWithFrame:CGRectMake(center.x-arrowW/2, center.y+imgH/2, arrowW, arrowH)]; 
     triangle.tag = kAnnotationCalloutArrowTag; 
     [self addSubview:triangle]; 

     [calloutBox setFrame:CGRectMake(0, center.y+imgH/2+arrowH, calloutBox.width, calloutBox.height)]; 
     calloutBox.tag = kAnnotationCalloutBoxTag; 
     [self addSubview:calloutBox]; 
    } 
    else 
    { 
     //return things back to normal 
     UIView* v = [self viewWithTag:kAnnotationCalloutBoxTag]; 
     [v removeFromSuperview]; 
     v = [self viewWithTag:kAnnotationCalloutArrowTag]; 
     [v removeFromSuperview]; 
     v = [self viewWithTag:kAnnotationTempImageViewTag]; 
     [v removeFromSuperview]; 

     self.image = icon; 
     self.bounds = CGRectMake(0, 0, [self unselectedSize].width, [self unselectedSize].height); 
    } 
    [super setSelected:selected animated:animated]; 
} 

-(CGSize)unselectedSize 
{ 
    return CGSizeMake(20,20); 
} 

-(UIView*)newCustomCallout 
{ 
    //create your own custom call out view 
    UIView* v = [[UIView alloc] initWithFrame:CGRectMake(0,0,250,250)]; 
    v.backgroundColor = [UIColor greenColor]; 
    return v; 
} 

-(UIView*)newTriangleWithFrame:(CGRect)frame 
{ 
    //create your own triangle 
    UIImageView* v = [[UIImageView alloc] initWithFrame:frame]; 
    [v setImage:[UIImage imageNamed:@"trianglePointedUp.png"]]; 
    return v; 
} 
0
(void)mapView:(MKMapView *)mapViewIn didSelectAnnotationView:(MKAnnotationView *)view { 
    if(![view.annotation isKindOfClass:[MKUserLocation class]]) 
    { 
     CustomeCalloutViewController *calloutView = [[CustomeCalloutViewController alloc]initWithNibName:@"CustomeCalloutViewController" bundle:nil]; 
     [calloutView setPopinTransitionStyle:BKTPopinTransitionStyleSlide]; 
     [calloutView setPopinTransitionDirection:BKTPopinTransitionDirectionTop]; 
     [self presentPopinController:calloutView animated:YES completion:^{ 
      NSLog(@"Popin presented !"); 
     }]; 
     [mapView deselectAnnotation:view.annotation animated:true]; 
    } 
}