【问题标题】:Custom View for MKPointAnnotationMKPointAnnotation 的自定义视图
【发布时间】:2020-10-25 07:47:41
【问题描述】:

我希望在地图上的某个点(折线的起点和终点)上方显示自定义视图。它看起来像这样: enter image description here

视图应该与加载时的视图完全相同,无需点击屏幕。

【问题讨论】:

  • 您需要注释视图和标签在您的图像中显示吗?还是愿意考虑标准标记视图?您需要支持哪些 iOS 版本?
  • 另外,请重新查看注释视图下方的标签和披露指示按钮。那会一直存在吗?例如,如果您点击地图上的其他地方,您希望它消失吗? IE。它是在选择和取消选择目标注释视图时出现和消失的标注,并且您只是希望它最初显示为选中状态,还是永远不会消失的永久视图?
  • @Rob 我需要视图像图像中一样出现。仅支持 iOS 12 及更高版本。

标签: ios swift mapkit


【解决方案1】:

基本思想是你继承MKAnnotationView并设置它的image

您可以以任何方式实现注释视图下方的按钮,但请确保实现hitTest(_:with:) 以便正确识别点击。

例如

class CustomAnnotationView: MKAnnotationView {

    static let lineWidth: CGFloat = 2
    static let pinSize = CGSize(width: 30, height: 37)
    static let pinImage: UIImage = image(with: .blue)

    private let button: UIButton = DisclosureIndicatorButton()

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

        image = Self.pinImage
        centerOffset = CGPoint(x: 0, y: -Self.pinSize.height / 2)

        configure(for: annotation)
        configureButton()
    }
        
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override var annotation: MKAnnotation? {
        didSet {
            configure(for: annotation)
        }
    }
    
    // this is required for a tap on the callout/button below the annotation view to be recognized

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard let hitView = super.hitTest(point, with: event) else {
            let pointInButton = convert(point, to: button)
            return button.hitTest(pointInButton, with: event)
        }

        return hitView
    }
}

private extension CustomAnnotationView {
    func configure(for annotation: MKAnnotation?) {
        canShowCallout = false
        button.setTitle(annotation?.title ?? "Unknown", for: .normal)

        // if you were also doing clustering, you do that configuration here ...
    }

    func configureButton() {
        addSubview(button)

        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: centerXAnchor),
            button.topAnchor.constraint(equalTo: bottomAnchor, constant: 20)
        ])

        button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)
    }

    /// Function to create pin image of the desired color
    ///
    /// You could obviously just create an pre-rendered image in your asset collection,
    /// but I find it just as easy to render them programmatically.

    static func image(with color: UIColor) -> UIImage {
        UIGraphicsImageRenderer(size: pinSize).image { _ in
            let bounds = CGRect(origin: .zero, size: pinSize)
            let rect = bounds.insetBy(dx: lineWidth / 2, dy: lineWidth / 2)
            let r = rect.width / 2
            let h = rect.height - r
            let theta = acos(r / h)
            let center = CGPoint(x: rect.midX, y: rect.midX)
            let path = UIBezierPath(arcCenter: center, radius: r, startAngle: .pi / 2 - theta, endAngle: .pi / 2 + theta, clockwise: false)
            path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
            path.lineWidth = lineWidth
            path.close()

            color.setFill()
            path.fill()
            UIColor.white.setStroke()
            path.stroke()

            let path2 = UIBezierPath(arcCenter: center, radius: r / 3, startAngle: 0, endAngle: .pi * 2, clockwise: true)
            UIColor.white.setFill()
            path2.fill()
        }
    }

    /// Button handler
    ///
    /// Note, taps on the button are passed to map view delegate via
    /// mapView(_:annotationView:calloutAccessoryControlTapped)`.
    ///
    /// Obviously, you could use your own delegate protocol if you wanted.

    @objc func didTapButton(_ sender: Any) {
        if let mapView = mapView, let delegate = mapView.delegate {
            delegate.mapView?(mapView, annotationView: self, calloutAccessoryControlTapped: button)
        }
    }

    /// Map view
    ///
    /// Navigate up view hierarchy until we find `MKMapView`.

    var mapView: MKMapView? {
        var view = superview
        while view != nil {
            if let mapView = view as? MKMapView { return mapView }
            view = view?.superview
        }
        return nil
    }
}

结果:

注释视图下方按钮的创建超出了问题的范围,可以像How do I put the image on the right side of the text in a UIButton? 中讨论的答案之一一样创建在上面的示例中,我刚刚将其中一种解决方案包装在其自己的班级,DisclosureIndicatorButton,但我真的不想卷入关于首选方法的辩论,所以我会让你选择任何你想要的。

【讨论】:

    【解决方案2】:

    你应该使用 MKMarkerAnnotationView,我发现这篇文章很有帮助:http://infinityjames.com/blog/mapkit-ios11

    主要是关于聚类,但也展示了如何使用您自己的图形创建一个点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多