我有一个MKMapView,上面带有几种不同类型的MKAnnotationView
。首先,我有一个PricedAnnotationView
,这是一个相当复杂的MKAnnotationView
,带有UILabel
和其他一些视图。标签可以基于与之关联的原始MKAnnotation
而具有不同的文本。
class PricedAnnotationView: MKAnnotationView {
static let ReuseID = "pricedAnnotation"
let labelContainingView: UIView = {
let containingView = UIView()
containingView.backgroundColor = .blue
containingView.layer.cornerRadius = 3
containingView.translatesAutoresizingMaskIntoConstraints = false
return containingView
}()
let label: UILabel = {
let label = UILabel(frame: CGRect.zero)
label.textColor = .white
label.font = ViewConstants.Text.BodyFontBold
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let containingView: UIView = {
let v = UIView(frame:CGRect.zero)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
let triangle = UIView(frame: CGRect(x: 0, y: 0, width: 9, height: 9))
triangle.translatesAutoresizingMaskIntoConstraints = false
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 5.5, y: 11))
path.addLine(to: CGPoint(x: 11, y: 0))
path.close()
let triangleLayer = CAShapeLayer()
triangleLayer.path = path.cgPath
triangleLayer.fillColor = UIColor.blue.cgColor
triangle.layer.addSublayer(triangleLayer)
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
self.canShowCallout = false
self.frame = labelContainingView.frame
labelContainingView.addSubview(label)
containingView.addSubview(labelContainingView)
containingView.addSubview(triangle)
self.addSubview(containingView)
// Get the label correctly inset in the labelContainingView
label.topAnchor.constraint(equalTo: labelContainingView.topAnchor, constant: 3).isActive = true
label.leadingAnchor.constraint(equalTo: labelContainingView.leadingAnchor, constant: 6).isActive = true
label.trailingAnchor.constraint(equalTo: labelContainingView.trailingAnchor, constant: -6).isActive = true
label.bottomAnchor.constraint(equalTo: labelContainingView.bottomAnchor, constant: -3).isActive = true
// The triangle.topAnchor purposefully puts the triangle a bit under the label. In testing, while moving around
// the map, a little gap would appear between the label and the triangle. This change fixes that. The triangle
// was made two pixels bigger so that it would appear to be the same size.
triangle.topAnchor.constraint(equalTo: labelContainingView.bottomAnchor, constant: -2).isActive = true
triangle.centerXAnchor.constraint(equalTo: labelContainingView.centerXAnchor).isActive = true
triangle.widthAnchor.constraint(equalToConstant: 11).isActive = true
triangle.heightAnchor.constraint(equalToConstant: 11).isActive = true
containingView.topAnchor.constraint(equalTo: labelContainingView.topAnchor).isActive = true
containingView.leadingAnchor.constraint(equalTo: labelContainingView.leadingAnchor).isActive = true
containingView.trailingAnchor.constraint(equalTo: labelContainingView.trailingAnchor).isActive = true
containingView.bottomAnchor.constraint(equalTo: triangle.bottomAnchor).isActive = true
}
/// - Tag: DisplayConfiguration
override func prepareForDisplay() {
super.prepareForDisplay()
displayPriority = .required
guard let annotation = annotation as? MyAnnotation else { return }
if case let .priced(price, currencyCode) = annotation.status {
label.text = StringFormatter.formatCurrency(amount: price, currencyCode: currencyCode)
}
self.layoutIfNeeded() // Calculate the size from the constraints so we can know the frame.
self.frame = containingView.frame
self.centerOffset = CGPoint(x: 0, y: -(containingView.frame.height / 2)) // Center point should be where the triangle points
}
}
其他注释都简单得多,仅使用两个UIImage
之一来支持自己。我将两个更简单的MKAnnotationView
用于不同的重用ID,因此不必继续设置图像;我不知道这是否是最佳做法。这是我的操作示例:
private let unpricedAnnotationClusterId = "unpriced"
private let unpricedAnnotationReuseID = "unpricedAnnotation"
func createUnpricedAnnotation(mapView: MKMapView, annotation: MKAnnotation) -> MKAnnotationView {
let annotationView: MKAnnotationView
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: unpricedAnnotationReuseID) {
annotationView = dequeuedAnnotationView
annotationView.annotation = annotation
} else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: unpricedAnnotationReuseID)
annotationView.image = UIImage(bundleAsset: "map_pin_blue")
annotationView.centerOffset = CGPoint(x: 0, y: -(annotationView.frame.height / 2)) // Center point should be where the pin points
annotationView.clusteringIdentifier = unpricedAnnotationClusterId
}
return annotationView
}
[当用户点击地图中的注释时,我在地图下方有一个信息窗口,其中包含有关所选项目的信息。很好如果注释视图发生了变化,我希望这样做,这样就可以清楚地显示出该项目在何处。只需将其设置为图像就可以了。但是,我找不到如何执行此操作的任何示例。大多数示例只是改变图像。当我尝试使用PricedAnnotationView
时,尽管两个更简单的注释都可以正常工作,但先前的标签并没有消失。我也不确定更改图像将如何影响我正在使用的重用ID。我看不到手动更改重用标识符的方法。
我的MKMapViewDelegate
具有以下功能:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if allowSelection {
if let item : MyAnnotation = view.annotation as? MyAnnotation {
...
// display selected item in information view
...
}
view.image = UIImage(bundleAsset: "map_pin_red")
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? MyAnnotation else {
return nil
}
switch annotation.status {
case .notPriced:
return createUnpricedAnnotation(mapView: mapView, annotation: annotation)
case .priced(_, _):
return PricedAnnotationView(annotation: annotation, reuseIdentifier: PricedAnnotationView.ReuseID)
default:
return createErrorAnnotation(mapView: mapView, annotation: annotation)
}
}
如果我可以只设置图像,那么在用户选择了其他内容之后,如何将其改回原样。如果以前是这些注释之一,是否需要从头开始重新创建PricedAnnotationView
?这对重用ID和重用队列有什么作用?
[当我刚刚更改图像时,PricedAnnotationView
的视图实际上并没有消失,而是仅移到侧面,如下图所示。
[理想情况下,我可以做一些事来触发对mapView(_:viewFor:)
的呼叫,并在那里有一些智能以记住该项目是现在还是现在被选中,但是我在研究中没有发现任何东西可以显示如何执行此操作。 (这并不是完全无关紧要的,因为isSelected
属性似乎没有在我刚刚出队的MKAnnotationView
上设置。在这里并不奇怪。)
关于如何处理此问题的任何建议将不胜感激。
我已经解决了这个问题,这是我为解决这个问题所做的工作:
我停止为这两种图像注释使用不同的重用ID。我创建了此功能来进行管理:
func createImageAnnotation(mapView: MKMapView, annotation: MKAnnotation, imageString: String) -> MKAnnotationView {
let annotationView: MKAnnotationView
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: imageAnnotationReuseID) {
annotationView = dequeuedAnnotationView
annotationView.annotation = annotation
} else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: imageAnnotationReuseID)
}
annotationView.image = UIImage(bundleAsset: imageString)
annotationView.centerOffset = CGPoint(x: 0, y: -(annotationView.frame.height / 2)) // Center point should be where the pin points
annotationView.clusteringIdentifier = imageAnnotationClusterId
return annotationView
}
然后我添加到选择和取消选择调用中,以便隐藏PricedAnnotationView
,并在适当时设置图像。像这样:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if allowSelection {
if let item : MyAnnotation = view.annotation as? MyAnnotation {
...
// display selected item in information view
...
view.image = UIImage(bundleAsset: selectedAnnotationImage)
if case .priced(_,_) = item.rateStatus {
(view as! PricedAnnotationView).containingView.isHidden = true
}
view.centerOffset = CGPoint(x: 0, y: -(view.frame.height / 2)) // Center point should be where the pin points
}
}
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
guard let annotation = view.annotation as? MyAnnotation else { return }
switch annotation.rateStatus {
case .notPriced:
view.image = UIImage(bundleAsset: unpricedAnnotationImage)
case .priced(_, _):
view.image = nil
if let myView = (view as? PricedAnnotationView) {
myView.containingView.isHidden = false
view.frame = myView.containingView.frame
view.centerOffset = CGPoint(x: 0, y: -(myView.containingView.frame.height / 2)) // Center point should be where the triangle points
}
default:
view.image = UIImage(bundleAsset: errorAnnotationImage)
}
}
这似乎在大多数情况下是有效的,唯一的怪癖是当PricedAnnotationView
更改为图像时,图像会短暂而明显地从旧帧尺寸缩小到正确的帧尺寸。但是,如果不设置边框,则PricedAnnotationView
会发生偏移。