【问题标题】:How to implement MKClusterAnnotations in Objective-C?如何在 Objective-C 中实现 MKClusterAnnotations?
【发布时间】:2019-02-01 18:57:14
【问题描述】:

我正在尝试为我的 Apple Map 上彼此非常接近的注释创建集群视图。我知道 Apple 在 iOS 11 中推出了原生集群视图工具包,但我在网上找到的所有教程都是用 Swift 编写的。我希望有人可以教我或推荐我可以阅读的任何教程,以了解如何在 Objective-C 中实现集群注释。

我的想法是创建一个ClusterView类,继承MKAnnotationView类,然后在mapView控制器中创建一个ClusterView的实例。

我已经阅读了苹果的文档,它只为您提供了我可能需要调用的函数,但没有说明如何使用它们,这是苹果文档的链接:https://developer.apple.com/documentation/mapkit/mkclusterannotation?language=objc

任何帮助将不胜感激!

【问题讨论】:

    标签: ios objective-c mkmapview mkannotation


    【解决方案1】:

    以下是基本步骤:

    1. 定义您的注释视图,指定clusteringIdentifiercollisionMode

      //  CustomAnnotationView.h
      
      @import MapKit;
      
      @interface CustomAnnotationView : MKMarkerAnnotationView
      
      @end
      

      //  CustomAnnotationView.m
      
      #import "CustomAnnotationView.h"
      
      static NSString *identifier = @"com.domain.clusteringIdentifier";
      
      @implementation CustomAnnotationView
      
      - (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
          if ((self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier])) {
              self.clusteringIdentifier = identifier;
              self.collisionMode = MKAnnotationViewCollisionModeCircle;
          }
      
          return self;
      }
      
      - (void)setAnnotation:(id<MKAnnotation>)annotation {
          [super setAnnotation:annotation];
      
          self.clusteringIdentifier = identifier;
      }
      
      @end
      
    2. 如果需要,您可以选择定义自己的集群注释视图,指定displayPrioritycollisionMode。这一个还更新了集群的图像以指示有多少注释被集群:

      //  ClusterAnnotationView.h
      
      @import MapKit;
      
      @interface ClusterAnnotationView : MKAnnotationView
      
      @end
      

      //  ClusterAnnotationView.m
      
      #import "ClusterAnnotationView.h"
      
      @implementation ClusterAnnotationView
      
      - (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
          if ((self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier])) {
              self.displayPriority = MKFeatureDisplayPriorityDefaultHigh;
              self.collisionMode = MKAnnotationViewCollisionModeCircle;
          }
      
          return self;
      }
      
      - (void)setAnnotation:(id<MKAnnotation>)annotation {
          super.annotation = annotation;
          [self updateImage:annotation];
      }
      
      - (void)updateImage:(MKClusterAnnotation *)cluster {
          if (!cluster) {
              self.image = nil;
              return;
          }
      
          CGRect rect = CGRectMake(0, 0, 40, 40);
          UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:rect.size];
          self.image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
              // circle
      
              [[UIColor blueColor] setFill];
              [[UIColor whiteColor] setStroke];
      
              UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
              path.lineWidth = 0.5;
              [path fill];
              [path stroke];
      
              // count
      
              NSString *text = [NSString stringWithFormat:@"%ld", (long) cluster.memberAnnotations.count];
              NSDictionary<NSAttributedStringKey, id> *attributes = @{
                  NSFontAttributeName: [UIFont preferredFontForTextStyle: UIFontTextStyleBody],
                  NSForegroundColorAttributeName: [UIColor whiteColor]
                                                                      };
              CGSize size = [text sizeWithAttributes:attributes];
              CGRect textRect = CGRectMake(rect.origin.x + (rect.size.width  - size.width)  / 2,
                                           rect.origin.y + (rect.size.height - size.height) / 2,
                                           size.width,
                                           size.height);
              [text drawInRect:textRect withAttributes:attributes];
          }];
      }
      
      @end
      

      如果您不想,您不必为集群创建自己的子类。但这只是说明了如果您选择这样做,您可以如何完全控制集群的外观。

    3. 然后你的视图控制器只需要注册适当的类就完成了(不需要地图视图委托):

      [self.mapView registerClass:[CustomAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultAnnotationViewReuseIdentifier];
      

      如果您想使用自定义集群视图,也可以注册:

      [self.mapView registerClass:[ClusterAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultClusterAnnotationViewReuseIdentifier];
      

      例如:

      //  ViewController.m
      
      #import “ViewController.h"
      
      @import MapKit;
      
      #import "CustomAnnotationView.h"
      #import "ClusterAnnotationView.h"
      
      @interface ViewController ()
      @property (weak, nonatomic) IBOutlet MKMapView *mapView;
      @end
      
      @implementation ViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
      
          [self configureMapView];
      }
      
      - (void)configureMapView {
          self.mapView.userTrackingMode = MKUserTrackingModeFollow;
      
          [self.mapView registerClass:[CustomAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultAnnotationViewReuseIdentifier];
          [self.mapView registerClass:[ClusterAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultClusterAnnotationViewReuseIdentifier];
      }
      
      // I’m going to search for restaurants and add annotations for those,
      // but do whatever you want
      
      - (void)performSearch {
          MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
          request.naturalLanguageQuery = @"restaurant";
          request.region = self.mapView.region;
      
          MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
          [search startWithCompletionHandler:^(MKLocalSearchResponse * _Nullable response, NSError * _Nullable error) {
              if (error) {
                  NSLog(@"%@", error);
                  return;
              }
      
              for (MKMapItem *mapItem in response.mapItems) {
                  MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
                  annotation.coordinate = mapItem.placemark.coordinate;
                  annotation.title = mapItem.name;
                  annotation.subtitle = mapItem.placemark.thoroughfare;
                  [self.mapView addAnnotation:annotation];
              }
          }];
      }
      
      @end
      

    产生:

    【讨论】:

    【解决方案2】:

    这是一个简单的几个步骤的基本示例

    1) 在viewDidLoad 中添加以下注释就可以了

    MKPointAnnotation *point1 = [[MKPointAnnotation alloc] init];
    CLLocationCoordinate2D c1;
    c1.latitude = 46.469391;
    c1.longitude = 30.740883;
    point1.coordinate = c1;
    point1.title = @"Minsk, Belarus";
    [self.mapView addAnnotation:point1];
    
    MKPointAnnotation *point2 = [[MKPointAnnotation alloc] init];
    CLLocationCoordinate2D c2;
    c2.latitude = 46.469391;
    c2.longitude = 30.740883;
    point2.coordinate = c2;
    point2.title = @"Odessa, Ukraine";
    [self.mapView addAnnotation:point2];
    

    2) 在 MKMapViewDelegate 的 mapView:viewForAnnotation 中为注释提供可重用的视图,如下所示:

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
        if ([annotation isKindOfClass:[MKPointAnnotation class]]) {
    
            MKMarkerAnnotationView* annotationView = (MKMarkerAnnotationView *) (MKMarkerAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:@"Jacky.S"];
            if (annotationView == nil) {
                annotationView = [[MKMarkerAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"Jacky.S"];
                annotationView.enabled = YES;
                annotationView.clusteringIdentifier = @"pins";
                // annotationView.glyphImage = [UIImage imageNamed:@"we can use a nice image instead of the default pins"];
            } else {
                annotationView.annotation = annotation;
                annotationView.clusteringIdentifier = @"pins";
            }
            return annotationView;
        }
        return nil;
    }
    

    不要忘记将MKMapViewDelegate 设置为UIViewController

    [self.mapView setDelegate:self];
    

    更新刚刚发布了一个gist,展示了如何继承MKMarkerAnnotationView

    【讨论】:

      猜你喜欢
      • 2011-04-14
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多