选择时更改自定义MKAnnotationView

问题描述 投票:-3回答:1

我有一个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的视图实际上并没有消失,而是仅移到侧面,如下图所示。

enter image description here

[理想情况下,我可以做一些事来触发对mapView(_:viewFor:)的呼叫,并在那里有一些智能以记住该项目是现在还是现在被选中,但是我在研究中没有发现任何东西可以显示如何执行此操作。 (这并不是完全无关紧要的,因为isSelected属性似乎没有在我刚刚出队的MKAnnotationView上设置。在这里并不奇怪。)

关于如何处理此问题的任何建议将不胜感激。

ios swift mkmapview mkannotation mkannotationview
1个回答
0
投票

我已经解决了这个问题,这是我为解决这个问题所做的工作:

我停止为这两种图像注释使用不同的重用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会发生偏移。

© www.soinside.com 2019 - 2024. All rights reserved.