【问题标题】:How to implement MKClusterAnnotations in Objective-C?如何在 Objective-C 中实现 MKClusterAnnotations?
【发布时间】:2019-02-01 18:57:14
【问题描述】:
【问题讨论】:
标签:
ios
objective-c
mkmapview
mkannotation
【解决方案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
-
如果需要,您可以选择定义自己的集群注释视图,指定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
如果您不想,您不必为集群创建自己的子类。但这只是说明了如果您选择这样做,您可以如何完全控制集群的外观。
-
然后你的视图控制器只需要注册适当的类就完成了(不需要地图视图委托):
[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