1

到目前为止,我已经在某些应用程序中看到了这一点,当用户缩小时,注释会彼此靠近,如果它们太靠近,则会被例如“+5”引脚或其他东西所取代。

怎么做?我认为应该regionDidChangeAnimated在 mapview 上检查每个 Pin 之间的距离(不是实际距离)。

那是正确的方法吗?如何获取地图视图上的距离而不是其中的距离(例如,纽约和旧金山之间的距离将始终相同,但如果用户缩小,地图上大头针之间的距离会缩小)

4

1 回答 1

1

在 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 分钟开始)。他们采用的概念是将可见地图划分为网格的概念,如果特定网格中有多个注释,他们会删除它们并添加单个“集群”注释。它们还说明了您甚至可以如何在视觉上动画化注释进出集群的移动,因此用户可以在放大和缩小时了解正在发生的事情。当您深入研究时,这是一个很好的起点。

于 2013-09-13T06:43:37.067 回答