自定义 MKAnnotation 标注视图?

Posted

技术标签:

【中文标题】自定义 MKAnnotation 标注视图?【英文标题】:Customize MKAnnotation Callout View? 【发布时间】:2015-06-11 23:54:01 【问题描述】:

我有一个MKPointAnnotation

let ann = MKPointAnnotation()
self.ann.coordinate = annLoc
self.ann.title = "Customize me"
self.ann.subtitle = "???"
self.mapView.addAnnotation(ann)

看起来像这样:

如何自定义此标注视图以创建自己的视图而不是预定义的视图?

【问题讨论】:

查看下面的答案,安娜的答案***.com/questions/25631410/… @casillas 不,这是自定义注释。他不是在尝试自定义注释视图,而是它的标注。我认为您需要更像 ***.com/a/17772487/1271826 的东西(它是 Objective-C,但在 Swift 中的想法是相同的)。 您还可以将自定义视图实现为标注。这可能是一个例子。 github.com/NabinRai4017/CallOutExample 你可以试试这个:github.com/okhanokbay/MapViewPlus 【参考方案1】:

首先应该注意的是,对标注的最简单更改是通过简单地调整系统提供的标注的属性来启用的,但是自定义左右配件(通过rightCalloutAccessoryViewleftCalloutAccessoryView)。您可以在viewForAnnotation 中进行该配置。

ios 9 开始,我们可以访问 detailCalloutAccessoryView,它将标注的副标题替换为可能具有丰富视觉效果的视图,同时仍然享受标注气泡的自动呈现(使用自动布局使这更容易)。

例如,这里有一个标注,它使用 MKSnapshotter 为细节标注附件中的图像视图提供图像,如 WWDC 2015 视频 What's New in MapKit 中所示:

您可以通过以下方式实现:

class SnapshotAnnotationView: MKPinAnnotationView 
    override var annotation: MKAnnotation?  didSet  configureDetailView()  

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) 
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        configure()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        configure()
    


private extension SnapshotAnnotationView 
    func configure() 
        canShowCallout = true
        configureDetailView()
    

    func configureDetailView() 
        guard let annotation = annotation else  return 

        let rect = CGRect(origin: .zero, size: CGSize(width: 300, height: 200))

        let snapshotView = UIView()
        snapshotView.translatesAutoresizingMaskIntoConstraints = false

        let options = MKMapSnapshotter.Options()
        options.size = rect.size
        options.mapType = .satelliteFlyover
        options.camera = MKMapCamera(lookingAtCenter: annotation.coordinate, fromDistance: 250, pitch: 65, heading: 0)

        let snapshotter = MKMapSnapshotter(options: options)
        snapshotter.start  snapshot, error in
            guard let snapshot = snapshot, error == nil else 
                print(error ?? "Unknown error")
                return
            

            let imageView = UIImageView(frame: rect)
            imageView.image = snapshot.image
            snapshotView.addSubview(imageView)
        

        detailCalloutAccessoryView = snapshotView
        NSLayoutConstraint.activate([
            snapshotView.widthAnchor.constraint(equalToConstant: rect.width),
            snapshotView.heightAnchor.constraint(equalToConstant: rect.height)
        ])
    

当然,您随后会将该注释视图注册到您的地图中,并且根本不需要 mapView(_:viewFor:)

mapView.register(SnapshotAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)

如果您正在寻找对标注进行更彻底的重新设计或需要支持 iOS 9 之前的版本,则需要做更多的工作。该过程需要 (a) 禁用默认标注; (b) 当用户点击现有的注释视图(即地图上的可视图钉)时添加您自己的视图。

然后复杂性出现在标注的设计中,您必须在其中绘制所有想要显示的内容。例如。如果你想画一个气泡来产生呼出的弹出感觉,你必须自己做。但是,如果您熟悉如何绘制形状、图像、文本等,您应该能够渲染实现所需 UX 的标注:

只需将视图添加为注释视图本身的子视图,并相应地调整其约束:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) 
    let calloutView = ...
    calloutView.translatesAutoresizingMaskIntoConstraints = false
    calloutView.backgroundColor = UIColor.lightGray
    view.addSubview(calloutView)

    NSLayoutConstraint.activate([
        calloutView.bottomAnchor.constraint(equalTo: view.topAnchor, constant: 0),
        calloutView.widthAnchor.constraint(equalToConstant: 60),
        calloutView.heightAnchor.constraint(equalToConstant: 30),
        calloutView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: view.calloutOffset.x)
    ])

有关创建您自己的标注视图的示例,请参阅https://github.com/robertmryan/CustomMapViewAnnotationCalloutSwift。这仅添加了两个标签,但它说明了您可以绘制任何您想要的形状的气泡,使用约束来决定标注的大小等。

【讨论】:

如何指定标注视图的尺寸?如何更改边框半径,可以在 calloutview 中使用 Autolayout 吗? 在我的示例中,我使用的是老式的而不是自动布局,实际上我使用字符串上的sizeWithAttributes 计算了文本的宽度,然后将每个角半径添加了两倍宽度(左右)和高度(顶部和底部),然后到高度,然后还在底部添加箭头的大小。 我没有看到你在哪里使用过sizeWithAttributes。能否请您说清楚,然后我可以接受您的回答。 Rob 你能帮帮我吗? ***.com/questions/34116577/… 如何设置 UIVIew 而不是 UIImageview?【参考方案2】:

无需制作 MKAnnotationView 自定义类,只需创建一个空视图 .xib 并根据您的要求设计 .xib。在 UIView swift 类中编写您的业务登录。

添加视图

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?

...

annotationView?.detailCalloutAccessoryView = customView 之类的方法

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? 
    let annotationIdentifier = "AnnotationIdentifier"
    var annotationView: MKAnnotationView?
    if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) 
        annotationView = dequeuedAnnotationView
        annotationView?.annotation = annotation
     else 
        annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
    
    if let annotation = annotation as? HPAnnotation 
       annotationView?.canShowCallout = true
       let customView = Bundle.main.loadNibNamed("HPAnnotationView", owner: self, options: nil)?.first as! HPAnnotationView
       customView.labelName.text = annotation.annotationTitle
       annotationView?.detailCalloutAccessoryView = customView
    
    return annotationView
 

如果您希望在标注视图上显示动态值,则首先制作 MKAnnotation 自定义类,您可以在其中根据需要传递对象。

import MapKit
import AddressBook
import UIKit

class HPAnnotation: NSObject, MKAnnotation 

   let title: String?
   let annotationTitle: String
   init(title: String, annotationTitle: String = "") 
      self.title = title
      self.annotationTitle = annotationTitle
   

   var subtitle: String? 
     return details
   

并在创建注解时传递值

 for index in 0..<searchPeopleArray.count 
    let annotation = HPAnnotation(title: "", annotationTitle: "")
    mapView.addAnnotation(annotation)

注意:这里的 HPAnnotationView 是我的自定义视图类和 xib 名称。 HPAnnotation 是我自定义的 MKAnnotation。

【讨论】:

【参考方案3】:

使用类类型 MKAnnotationView 创建 Cocoa 文件

CustomeAnnotationView.h 文件

@interface CustomeAnnotationView : MKAnnotationView
@property (strong, nonatomic) UIButton *buttonCustomeCallOut;
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
@end

CustomeAnnotationView.m 文件

@implementation CustomeAnnotationView

-(id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
        // Initialization code
    
    return self;



- (void)setSelected:(BOOL)selected animated:(BOOL)animated

    [super setSelected:selected animated:animated];

    if(selected)
    



            self.buttonCustomeCallOut = [UIButton buttonWithType:UIButtonTypeCustom];//iconShare//iconShareBlue

            [self.buttonCustomeCallOut addTarget:self action:@selector(buttonHandlerCallOut:) forControlEvents:UIControlEventTouchDown];
        [self.buttonCustomeCallOut setBackgroundColor:[UIColor blueColor]];

            [self.buttonCustomeCallOut setFrame:CGRectMake(-40,-80, 100, 100)];



            [self addSubview:self.buttonCustomeCallOut];

        [self.buttonCustomeCallOut setUserInteractionEnabled:YES];
    
    else
    
        //Remove your custom view...
        [self.buttonCustomeCallOut setUserInteractionEnabled:NO];
        [self.buttonCustomeCallOut removeFromSuperview];

        self.buttonCustomeCallOut=nil;
    

-(void)buttonHandlerCallOut:(UIButton*)sender
    NSLog(@"Annotation Clicked");


- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event

    UIView* v = [super hitTest:point withEvent:event];
    if (v != nil)
    
        [self.superview bringSubviewToFront:self];
    
    return v;


- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event

    CGRect rec = self.bounds;
    BOOL isIn = CGRectContainsPoint(rec, point);
    if(!isIn)
    
        for (UIView *v in self.subviews)
        
            isIn = CGRectContainsPoint(v.frame, point);
            if(isIn)
                break;
        
    
    return isIn;

@end

将此代码放置在您要创建客户调用的地方

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation 
    static NSString *identifier = @"CustAnnotation";

        CustomeAnnotationView *annotationView = (CustomeAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (annotationView == nil) 
            annotationView = [[CustomeAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
        

        annotationView.enabled = YES;
        annotationView.canShowCallout = NO;
        annotationView.centerOffset = CGPointMake(0,-10);//-18

        return annotationView;

【讨论】:

以上是关于自定义 MKAnnotation 标注视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何添加在图形页面定制标注视图

如何为 MkAnnotation View 自定义视图,就像表格视图的自定义单元格一样?

如何从注释标注Xcode 9设置警报

添加自定义注释引脚

MKAnnotation - 地图图钉标注弹出在其他地图图钉后面

如何在地图视图中添加自定义标注视图