自定义annotationView标签重叠(导致MKMapView内存泄露)

custom annotationView label overlap (causing MKMapView memory leak)

我正在使用 mkmapview 构建应用程序。我正在使用自定义注释视图来显示头像和相应的标签。一切正常:每个头像和相应的标签都正确显示。我可以在地图中导航。但是,当我最大程度地缩小时,假设在旧金山显示了 4 个标签,一旦我恢复到原始比例,标签就会重叠。 在最大缩小标签是在彼此的顶部。这个是正常的。但是一旦我恢复正常缩放:

   let span = MKCoordinateSpanMake(0.001, 0.001)

每个标签看起来都被其他标签盖章了:

上面的标签应该是 "sanchez" 但它已经与其他标签文本融为一体。

这是我的自定义注释视图代码:

   func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    if !(annotation is MKPointAnnotation) {
        return nil
    }
    var seleccion:Bool

    let reuseId = "test"
    var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
    if anView == nil {
        anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        anView.canShowCallout = true
    }
    else {
        anView.annotation = annotation
    }

    let cpa = annotation as! CustomPointAnnotation
    anView.image = cpa.image
    if cpa.toBeTriggered == true {
        anView.selected = true
    }
    var nameLbl: UILabel! = UILabel(frame: CGRectMake(-24, 40, 100, 30))
    nameLbl.text = cpa.nickName
    nameLbl.textColor = UIColor.blackColor()
    nameLbl.font = UIFont(name: "Atari Classic Extrasmooth", size: 10)
    nameLbl.textAlignment = NSTextAlignment.Center
    anView.addSubview(nameLbl)

    return anView
}

这就是奇怪的地方:如果我为 nameLbl 设置背景颜色,标签不会 overlap/melt。这让我想起了 Apple 的一个错误...

编辑

正如@Anna 提到的,CustomPointAnnotation 是一个数据模型class:

import UIKit
import MapKit

class CustomPointAnnotation: MKPointAnnotation {
    var image: UIImage!
    var toBeTriggered: Bool = false
    var selected: Bool = false
    var nickName: String!
}

有两个问题导致标签重叠:

  1. 如评论中所述,每次调用 viewForAnnotation 时都会将 UILabel 添加到注释视图中 正在回收视图(当 mapView.dequeueReusableAnnotationViewWithIdentifier returns 视图时)。回收视图将已经有一个 UILabel 并在其上设置了文本。当您在其上添加另一个标签时,您会得到重叠的文本。

    不是每次都添加标签,只有在实际创建一个MKAnnotationView时才添加它(当dequeue returns nil)。然后设置标签的 text,获取对视图中已有标签的引用(在其上设置 tag 是一种简单的方法)。

    另一种方法是创建自定义注释 view class(MKAnnotationView 的子class)并实现 prepareForReuse 并清除标签的文本。此自定义视图 class 将与 CustomPointAnnotation 模型 class.

  2. 分开
  3. 即使在问题 1 中进行了修复后,您仍然会在放大时看到重叠的标签,并且有多个注释彼此靠近。他们的图像和标签会重叠。这不是错误。就是那样子。为避免此问题(如果这对您来说是个问题),一种解决方案是实施注释聚类,根据缩放级别合并或分离注释。解释如何做到这一点超出了这个问题的范围。有兴趣的可以搜索注解聚类库或实现细节。

要以最快的方式解决问题 1,您可以执行以下操作:

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    if !(annotation is CustomPointAnnotation) {
        //Check for CustomPointAnnotation (not MKPointAnnotation)
        //because the code below assumes CustomPointAnnotation.
        return nil
    }
    var seleccion:Bool

    let reuseId = "test"
    var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
    if anView == nil {
        anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        anView.canShowCallout = true

        //Create and add UILabel only when actually creating MKAnnotationView...
        var nameLbl: UILabel! = UILabel(frame: CGRectMake(-24, 40, 100, 30))
        nameLbl.tag = 42    //set tag on it so we can easily find it later
        nameLbl.textColor = UIColor.blackColor()
        nameLbl.font = UIFont(name: "Atari Classic Extrasmooth", size: 10)
        nameLbl.textAlignment = NSTextAlignment.Center
        anView.addSubview(nameLbl)
    }
    else {
        anView.annotation = annotation
    }

    let cpa = annotation as! CustomPointAnnotation
    anView.image = cpa.image

    //NOTE: Setting selected property directly on MKAnnotationView 
    //      is not recommended.
    //      See documentation for the property.
    //      Instead, call MKMapView.selectAnnotation method 
    //      in the didAddAnnotationViews delegate method.
    if cpa.toBeTriggered == true {
        anView.selected = true
    }

    //Get a reference to the UILabel already on the view
    //and set its text...
    if let nameLbl = anView.viewWithTag(42) as? UILabel {
        nameLbl.text = cpa.nickName
    }

    return anView
}

每次调用时使用不同的重用标识符

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?

获取一个计数器变量并将其作为字符串传递给 annotationIdentifier

mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier)