自定义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!
}
有两个问题导致标签重叠:
如评论中所述,每次调用 viewForAnnotation
时都会将 UILabel
添加到注释视图中 正在回收视图(当 mapView.dequeueReusableAnnotationViewWithIdentifier
returns 视图时)。回收视图将已经有一个 UILabel
并在其上设置了文本。当您在其上添加另一个标签时,您会得到重叠的文本。
不是每次都添加标签,只有在实际创建一个MKAnnotationView
时才添加它(当dequeue
returns nil
)。然后设置标签的 text
,获取对视图中已有标签的引用(在其上设置 tag
是一种简单的方法)。
另一种方法是创建自定义注释 view class(MKAnnotationView
的子class)并实现 prepareForReuse
并清除标签的文本。此自定义视图 class 将与 CustomPointAnnotation
模型 class.
分开
- 即使在问题 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)
我正在使用 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!
}
有两个问题导致标签重叠:
如评论中所述,每次调用
viewForAnnotation
时都会将UILabel
添加到注释视图中 正在回收视图(当mapView.dequeueReusableAnnotationViewWithIdentifier
returns 视图时)。回收视图将已经有一个UILabel
并在其上设置了文本。当您在其上添加另一个标签时,您会得到重叠的文本。不是每次都添加标签,只有在实际创建一个
MKAnnotationView
时才添加它(当dequeue
returnsnil
)。然后设置标签的text
,获取对视图中已有标签的引用(在其上设置tag
是一种简单的方法)。另一种方法是创建自定义注释 view class(
MKAnnotationView
的子class)并实现prepareForReuse
并清除标签的文本。此自定义视图 class 将与CustomPointAnnotation
模型 class. 分开
- 即使在问题 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)