Как реализовать MKClusterAnnotations в Objective-C? - PullRequest
0 голосов
/ 01 февраля 2019

Я пытаюсь создать кластерные представления для аннотаций, которые очень близки друг к другу на моей карте Apple.Я знаю, что у Apple есть собственный набор представлений кластеров, выпущенный с iOS 11, но все учебники, которые я смог найти в Интернете, написаны на Swift.Я надеюсь, что кто-нибудь может научить меня или порекомендовать мне любые учебники, которые я смог прочитать, чтобы узнать, как реализовать кластерные аннотации в Objective-C.

Моя идея - создать класс ClusterView, который наследует класс MKAnnotationView, а затем создать экземпляр ClusterView в контроллере mapView.

Я прочитал документацию от Apple, она предоставляет только те функции, которые мне могут понадобиться вызывать, но не объясняет, как их использовать, это ссылка на документацию Apple: https://developer.apple.com/documentation/mapkit/mkclusterannotation?language=objc

Буду признателен за любую помощь!

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

Вот основные шаги:

  1. Определите свой вид аннотации, указав clusteringIdentifier и collisionMode:

    //  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. При желании вы можете задать свой собственный вид аннотации кластера, указав displayPriority и collisionMode.Это также обновляет образ для кластера, чтобы указать, сколько аннотаций кластеризовано:

    //  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
    

Что дает:

demo

0 голосов
/ 01 февраля 2019

Вот простой базовый пример, состоящий из пары шагов

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) В mapView: viewForAnnotation MKMapViewDelegateобеспечить возможность многократного просмотра аннотаций, например:

- (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];

Обновление Только что закончили публиковать Гист показывает, как подкласс MKMarkerAnnotationView

...