到目前为止,我已经在某些应用程序中看到了这一点,当用户缩小时,注释会彼此靠近,如果它们太靠近,则会被例如“+5”引脚或其他东西所取代。
怎么做?我认为应该regionDidChangeAnimated
在 mapview 上检查每个 Pin 之间的距离(不是实际距离)。
那是正确的方法吗?如何获取地图视图上的距离而不是其中的距离(例如,纽约和旧金山之间的距离将始终相同,但如果用户缩小,地图上大头针之间的距离会缩小)
到目前为止,我已经在某些应用程序中看到了这一点,当用户缩小时,注释会彼此靠近,如果它们太靠近,则会被例如“+5”引脚或其他东西所取代。
怎么做?我认为应该regionDidChangeAnimated
在 mapview 上检查每个 Pin 之间的距离(不是实际距离)。
那是正确的方法吗?如何获取地图视图上的距离而不是其中的距离(例如,纽约和旧金山之间的距离将始终相同,但如果用户缩小,地图上大头针之间的距离会缩小)
在 WWDC 2017 What's New in MapKit 中,他们向我们介绍了 iOS 11 中的一个新的注释集群API,这使得实现集群变得非常容易。简而言之,clusteringIdentifier
为您的注释视图设置一个,它会为您处理所有的集群逻辑。例如
override func viewDidLoad() {
super.viewDidLoad()
mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(ClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
...
}
而且,在这个过程中,不需要任何MKMapViewDelegate
方法(尽管如果您想要进一步定制,显然可以这样做,但是如果您对基本 UX 没问题,默认的重用标识符消除了对它的需要。关键是,您必须实现您的注释视图,特别是clusteringIdentifier
为主注释视图的设置,因此集群将自动发生,例如
class CustomAnnotationView: MKMarkerAnnotationView {
static let clusteringIdentifier = "ClusterAnnotationView"
let annotationWidth = 40
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
clusteringIdentifier = CustomAnnotationView.clusteringIdentifier
collisionMode = .circle
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
willSet {
clusteringIdentifier = CustomAnnotationView.clusteringIdentifier
// you can do whatever other update to your `newValue` annotation needed here, if you'd like
}
}
}
和
class ClusterAnnotationView: MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
displayPriority = .defaultHigh
collisionMode = .circle
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
willSet {
updateImage(for: newValue as? MKClusterAnnotation)
}
}
private func updateImage(for cluster: MKClusterAnnotation?) {
guard let cluster = cluster else { image = nil; return }
let rect = CGRect(origin: .zero, size: CGSize(width: 40, height: 40))
let renderer = UIGraphicsImageRenderer(size: rect.size)
image = renderer.image { _ in
// e.g. circle
#colorLiteral(red: 0.1215686277, green: 0.01176470611, blue: 0.4235294163, alpha: 1).setFill()
#colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0).setStroke()
let path = UIBezierPath(arcCenter: rect.center, radius: rect.radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
path.lineWidth = 0.5
path.fill()
path.stroke()
// with count in the center
let text = "\(cluster.memberAnnotations.count)"
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.white,
.font: UIFont.boldSystemFont(ofSize: 20)]
let size = text.size(withAttributes: attributes)
let textRect = CGRect(origin: CGPoint(x: rect.midX - size.width / 2, y: rect.midY - size.height / 2), size: size)
text.draw(in: textRect, withAttributes: attributes)
}
}
}
不再需要我在下面的原始答案中考虑的所有复杂的手动聚类。
在 WWDC 2011 Visualizing Information Geographically with MapKit中,他们说明了一种方法来精确地做到这一点(演示的内容大约从视频开始 18 分钟开始)。他们采用的概念是将可见地图划分为网格的概念,如果特定网格中有多个注释,他们会删除它们并添加单个“集群”注释。它们还说明了您甚至可以如何在视觉上动画化注释进出集群的移动,因此用户可以在放大和缩小时了解正在发生的事情。当您深入研究时,这是一个很好的起点。