当用户缩小并且注释彼此太靠近时隐藏MKAnnotations

时间:2013-09-13 06:19:33

标签: ios objective-c mkmapview

到目前为止,我已经在一些应用程序中看到过这种情况,当用户缩小时,注释会彼此靠得更近,如果它们太近,那么它们就会被例如替换。 <+ 5'引脚或其他东西。

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

这是正确的做法吗?如何获取mapview上的距离而不是它内部的距离(例如NY和SF之间的距离将始终相同,但如果用户缩小,地图上的引脚之间的距离会缩小)

1 个答案:

答案 0 :(得分:1)

在2017年WWDC What's New in MapKit中,他们向我们介绍了iOS 11中的新annotation clustering API,这使得实现群集非常容易。简而言之,为注释视图设置clusteringIdentifier,它会为您处理所有聚类逻辑。 E.g。

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