热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

将标注/语音气泡添加到MapView的用户注释中

如何解决《将标注/语音气泡添加到MapView的用户注释中》经验,为你挑选了1个好方法。

现在,我的应用程序显示一个customUserAnnotationView,其中包含用户批注的自定义图像(您可以在ViewController.swift中看到它)。我还创建了一个自定义UIView,希望将其用作用户注释上方的注释(其代码和图像位于SpeechBubble.swift下)。

我想结合这两个对象,以便可以显示CustomUserAnnotationView和上面的注释中放置的Custom UIView(SpeechBubble.swift)。

我尝试从 多个 mapbox 教程制作Frankenstein程序的尝试对我来说还没有成功。我只想将我创建的自定义注释类放在图像上方,并可能添加一个小三角形以使其看起来像气泡。


ViewController.swift

import Mapbox

class ViewController: UIViewController, MGLMapViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        let mapView = MGLMapView(frame: view.bounds)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.delegate = self

        // Enable heading tracking mode so that the arrow will appear.
        mapView.userTrackingMode = .followWithHeading

        // Enable the permanent heading indicator, which will appear when the tracking mode is not `.followWithHeading`.
        mapView.showsUserHeadingIndicator = true

        view.addSubview(mapView)

        let idea =  UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
        idea.text = "Hello There"
        idea.textAlignment = NSTextAlignment.center

        let sb = SpeechBubble(coord: mapView.targetCoordinate, idea: idea)
        mapView.addSubview(sb)
    }

    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
        return true
    }

    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
        // Substitute our custom view for the user location annotation. This custom view is defined below.
        if annotation is MGLUserLocation && mapView.userLocation != nil {
            return Avatar()
        }
        return nil
    }

    // Optional: tap the user location annotation to toggle heading tracking mode.
    func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
        if mapView.userTrackingMode != .followWithHeading {
            mapView.userTrackingMode = .followWithHeading
        } else {
            mapView.resetNorth()
        }

        // We're borrowing this method as a gesture recognizer, so reset selection state.
        mapView.deselectAnnotation(annotation, animated: false)
    }
}


SpeechBubble.swift

import UIKit
import Mapbox

class SpeechBubble: UIView, MGLMapViewDelegate{

    //var sbView: UIView

    init(coord: CLLocationCoordinate2D, idea: UITextView) {

        let width = CGFloat(180)
        let height = UITextField.layoutFittingExpandedSize.height + 32
        super.init(frame: CGRect(x: CGFloat(coord.latitude), y: CGFloat(coord.longitude), width: width, height: height))

        self.addSubview(idea)
        self.addSubview(buttonsView());
        self.addSubview(upvoteView());
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func upvoteView() -> UIView {
        let uView = UIView()

        let vCnt = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
        vCnt.center = CGPoint(x: 10.5, y: 32)
        vCnt.textAlignment = .center
        vCnt.text = "0"

        let uButton  = UIButton(type: .custom)
        uButton.frame = CGRect(x: vCnt.frame.size.width + 5, y: 0, width: 32, height: 32);
        let uImage = UIImage (named: "Upvote")
        uButton.setImage(uImage, for: .normal)

        uView.frame.size.width = vCnt.frame.size.width + uButton.frame.size.width + 5
        uView.frame.size.height = max(vCnt.frame.size.height, uButton.frame.size.height)

        uView.frame = CGRect(
            x: 0,
            y: self.frame.size.height - uView.frame.size.height,
            width: uView.frame.size.width,
            height: uView.frame.size.height );
        uView.addSubview(vCnt)
        uView.addSubview(uButton)
        return uView
    }

    func buttonsView() -> UIView {
        let bView = UIView()

        let jButton  = UIButton(type: .custom)
        rButton.frame = CGRect(x: 0, y: 0, width: 35, height: 32);
        let rImage = UIImage (named: "Rocket")
        rButton.setImage(rImage, for: .normal)

        let pButton  = UIButton(type: .custom)
        pButton.frame = CGRect(x: jButton.frame.size.width + 5, y: 0, width: 31, height: 36);
        let pImage = UIImage (named: "Profile")
        pButton.setImage(pImage, for: .normal)

        bView.frame.size.width = rButton.frame.size.width + pButton.frame.size.width + 5
        bView.frame.size.height = max(rButton.frame.size.height, pButton.frame.size.height)

        bView.frame = CGRect(
            x: self.frame.size.width - bView.frame.size.width,
            y: self.frame.size.height - bView.frame.size.height,
            width: bView.frame.size.width,
            height: bView.frame.size.height );
        bView.addSubview(rButton)
        bView.addSubview(pButton)
        return bView
    }

}


头像迅捷

import Mapbox

class Avatar: MGLUserLocationAnnotationView {
    let size: CGFloat = 48
    var arrow: CALayer!
    //var arrow: CAShapeLayer!

    // -update is a method inherited from MGLUserLocationAnnotationView. It updates the appearance of the user location annotation when needed. This can be called many times a second, so be careful to keep it lightweight.
    override func update() {
        if frame.isNull {
            frame = CGRect(x: 0, y: 0, width: size, height: size)
            return setNeedsLayout()
        }

        // Check whether we have the user’s location yet.
        if CLLocationCoordinate2DIsValid(userLocation!.coordinate) {
            setupLayers()
            updateHeading()
        }
    }

    private func updateHeading() {
        // Show the heading arrow, if the heading of the user is available.
        if let heading = userLocation!.heading?.trueHeading {
            arrow.isHidden = false

            // Get the difference between the map’s current direction and the user’s heading, then convert it from degrees to radians.
            let rotation: CGFloat = -MGLRadiansFromDegrees(mapView!.direction - heading)

            // If the difference would be perceptible, rotate the arrow.
            if abs(rotation) > 0.01 {
                // Disable implicit animations of this rotation, which reduces lag between changes.
                CATransaction.begin()
                CATransaction.setDisableActions(true)
                arrow.setAffineTransform(CGAffineTransform.identity.rotated(by: rotation))
                CATransaction.commit()
            }
        } else {
            arrow.isHidden = true
        }
    }

    private func setupLayers() {
        // This dot forms the base of the annotation.
        if arrow == nil {
            arrow = CALayer()
            let myImage = UIImage(named: "will_smith")?.cgImage
            arrow.bounds = CGRect(x: 0, y: 0, width: size, height: size)
            arrow.cOntents= myImage
            layer.addSublayer(arrow)
        }

    }

    // Calculate the vector path for an arrow, for use in a shape layer.
    private func arrowPath() -> CGPath {
        let max: CGFloat = size / 2
        let pad: CGFloat = 3

        let top =    CGPoint(x: max * 0.5, y: 0)
        let left =   CGPoint(x: 0 + pad,   y: max - pad)
        let right =  CGPoint(x: max - pad, y: max - pad)
        let center = CGPoint(x: max * 0.5, y: max * 0.6)

        let bezierPath = UIBezierPath()
        bezierPath.move(to: top)
        bezierPath.addLine(to: left)
        bezierPath.addLine(to: center)
        bezierPath.addLine(to: right)
        bezierPath.addLine(to: top)
        bezierPath.close()

        return bezierPath.cgPath
    }
}

-------------------------------------------------- -------------------------------------------------- ----




更新 我试图创建Answer和我的代码的科学怪人程序,并Property 'self.representedObject' not initialized at super.init call在SpeechBubble.swift中收到以下错误。我还将所有旧代码从SpeechBubble.swift移入insideSpeechBubble.swift

更新了SpeechBubble.swift

    import UIKit
    import Mapbox

    class SpeechBubble: UIView, MGLCalloutView {

        // Your IBOutlets //

        var representedObject: MGLAnnotation
        var annotationPoint: CGPoint
        // Required views but unused for this implementation.
        lazy var leftAccessoryView = UIView()
        lazy var rightAccessoryView = UIView()
        var contentView: MGLMapView
        weak var delegate: MGLCalloutViewDelegate?

        // MARK: - init methods

        required init(annotation: MGLAnnotation, frame: CGRect, annotationPoint: CGPoint) {
            let idea =  UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
            idea.text = "Hello There"
            idea.textAlignment = NSTextAlignment.center
            self.representedObject = annotation
            self.annotatiOnPoint= annotationPoint
            cOntentView= InsideSpeechBubble(coord: annotationPoint, idea: idea )
            super.init(frame: frame)
            commonInit()
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }

        private func commonInit() {
            Bundle.main.loadNibNamed("SpeechBubble", owner: self, options: nil)
            addSubview(contentView as UIView)
            contentView.frame = self.bounds

            // Do your initialisation //
        }

        // MARK: - MGLCalloutView methods

        func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
            // Present the custom callout slightly above the annotation's view. Initially invisble.
            self.center = annotationPoint.applying(CGAffineTransform(translationX: 0, y: -self.frame.height - 20.0))

            // I have logic here for setting the correct image and button states //
        }

        func dismissCallout(animated: Bool) {
            removeFromSuperview()
    }
}

更新了ViewController.swift

import Mapbox


class ViewController: UIViewController, MGLMapViewDelegate {
    //let point = MGLPointAnnotation()

    override func viewDidLoad() {
        super.viewDidLoad()
        let mapView = MGLMapView(frame: view.bounds)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.delegate = self

        // Enable heading tracking mode so that the arrow will appear.
        mapView.userTrackingMode = .followWithHeading

        // Enable the permanent heading indicator, which will appear when the tracking mode is not `.followWithHeading`.
        mapView.showsUserHeadingIndicator = true

        view.addSubview(mapView)

        let HighDea =  UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
        HighDea.text = "Hello There"
        HighDea.textAlignment = NSTextAlignment.center

        //let sb = SpeechBubble()
        //mapView.addSubview(sb)
    }

    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
        return true
    }

    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
        // Substitute our custom view for the user location annotation. This custom view is defined below.
        if annotation is MGLUserLocation && mapView.userLocation != nil {
            return Avatar()
        }
        return nil
    }

    func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
        // Do your annotation-specific preparation here //

        // I get the correct size from my xib file.
        let viewFrame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 261.0, height: 168.0))
        // Get the annotation's location in the view's coordinate system.
        let annotatiOnPoint= mapView.convert(annotation.coordinate, toPointTo: nil)

        let customCalloutView = SpeechBubble(annotation: annotation, frame: viewFrame, annotationPoint: annotationPoint)

        return customCalloutView
    }

//    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
        // This example is only concerned with point annotations.
//        guard annotation is MGLPointAnnotation else {
//            return nil
//        }

        // Use the point annotation’s longitude value (as a string) as the reuse identifier for its view.
//        let reuseIdentifier = "\(annotation.coordinate.longitude)"

        // For better performance, always try to reuse existing annotations.
//        var annotatiOnView= mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)

        // If there’s no reusable annotation view available, initialize a new one.
//        if annotatiOnView== nil {
//            annotatiOnView= CustomAnnotationView(reuseIdentifier: reuseIdentifier)
//            annotationView!.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)

            // Set the annotation view’s background color to a value determined by its longitude.
  //          let hue = CGFloat(annotation.coordinate.longitude) / 100
  //          annotationView!.backgroundColor = UIColor(hue: hue, saturation: 0.5, brightness: 1, alpha: 1)
  //      }

  //      return annotationView
  //  }

    // Optional: tap the user location annotation to toggle heading tracking mode.
    func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
        if mapView.userTrackingMode != .followWithHeading {
            mapView.userTrackingMode = .followWithHeading
        } else {
            mapView.resetNorth()
        }

        // We're borrowing this method as a gesture recognizer, so reset selection state.
        mapView.deselectAnnotation(annotation, animated: false)
    }
}

Magnas.. 5

当我为Mapbox注释实现自定义标注时,我使用了xib文件来设计实际的标注。我发现,与尝试从代码中构造UI相比,它给我提供了更多的即时反馈(但显然可以随您的喜好进行操作)。

这给了我类似以下内容。

使用UIImage作为背景,可以实现我选择的任何形状。在这里,我使用白色周围的透明度为您提供问题中提到的圆形元素和底部三角形。

此UIView的Swift文件(您的SpeechBubble)需要符合MGLCalloutView协议,而不是MGLMapViewDelegate您当前所拥有的。您ViewControllerMGLMapViewDelegate,而不是您的自定义标注。在IB中的Identity Inspector中以通常的方式将xib文件和Swift文件配对。所以会是这样的:

    import UIKit
    import Mapbox

    class SpeechBubble: UIView, MGLCalloutView {

    // Your IBOutlets //
    @IBOutlet var contentView: UIView! // The custom callout's view.

    var representedObject: MGLAnnotation
    var annotationPoint: CGPoint
    // Required views but unused for this implementation.
    lazy var leftAccessoryView = UIView()
    lazy var rightAccessoryView = UIView()

    weak var delegate: MGLCalloutViewDelegate?

    // MARK: - init methods

    required init(annotation: YourAnnotation, frame: CGRect, annotationPoint: CGPoint) {
        self.representedObject = annotation
        self.annotatiOnPoint= annotationPoint

        super.init(frame: frame)

        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonInit()
    }

    private func commonInit() {
        Bundle.main.loadNibNamed("SpeechBubble", owner: self, options: nil)
        addSubview(contentView)
        contentView.frame = self.bounds

        // Do your initialisation //
    }

    // MARK: - MGLCalloutView methods

    func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
        // Present the custom callout slightly above the annotation's view. Initially invisble.
        self.center = annotationPoint.applying(CGAffineTransform(translationX: 0, y: -self.frame.height - 20.0))

        // I have logic here for setting the correct image and button states //
    }

    func dismissCallout(animated: Bool) {
        removeFromSuperview()
    }

然后,您似乎只是缺少了在请求时MGLMapViewDelegate实际返回SpeechBubble视图的方法。它应该在您的ViewController文件中。

    func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
        // Do your annotation-specific preparation here //

        // I get the correct size from my xib file.
        let viewFrame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 261.0, height: 168.0))
        // Get the annotation's location in the view's coordinate system.
        let annotatiOnPoint= mapView.convert(annotation.coordinate, toPointTo: nil)
        let customCalloutView = SpeechBubble(annotation: YourAnnotation, frame: viewFrame, annotationPoint: annotationPoint)

        return customCalloutView
    }

希望这将使您更接近实现自己的目标。顺便说一句,这个问题的版本比第一个要领先几英里。

编辑+++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++

在没有看到您的项目的情况下几乎不可能完成这项工作,因此我整理了一个基本的实现。它基于此处的Mapbox示例:Mapbox自定义标注,由于某种原因,它没有显示如何实际提供标注视图。我还扩展了它以允许自定义注释图像。如果可以进行此工作,则应该能够将相关部分移至您自己的项目中。

我强烈建议您,如果尝试实施以下内容,请在一个新项目中进行。

视图控制器。

    import Mapbox

    class ViewController: UIViewController, MGLMapViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let mapView = MGLMapView(frame: view.bounds, styleURL: MGLStyle.lightStyleURL)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.tintColor = .darkGray
        view.addSubview(mapView)

        // Set the map view‘s delegate property.
        mapView.delegate = self

        // Initialize and add the marker annotation.
        let coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 0)
        let marker = MyAnnotation(coordinate: coordinate, title: "Bingo", subtitle: "Bongo")

        // Add marker to the map.
        mapView.addAnnotation(marker)
    }

    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
        return true
    }

    func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
        // Instantiate and return our custom callout view.
        let annotatiOnPoint= mapView.convert(annotation.coordinate, toPointTo: nil)
        let viewFrame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 250.0, height: 178.0))
        return CustomCalloutView(representedObject: annotation, frame: viewFrame, annotationPoint: annotationPoint)
    }

    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
        if let annotatiOnView= mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotationView") {
            return annotationView
        } else {
            let annotatiOnView= MyAnnotationView(reuseIdentifier: "myAnnotationView", size: CGSize(width: 45, height: 45), annotation: annotation)
            return annotationView
        }
    }

    func mapView(_ mapView: MGLMapView, tapOnCalloutFor annotation: MGLAnnotation) {
        // Optionally handle taps on the callout.
        print("Tapped the callout for: \(annotation)")

        // Hide the callout.
        mapView.deselectAnnotation(annotation, animated: true)
    }
}

CustomCalloutView.swift

import UIKit
import Mapbox

class CustomCalloutView: UIView, MGLCalloutView {

@IBOutlet var contentView: UIView!

weak var delegate: MGLCalloutViewDelegate?

var representedObject: MGLAnnotation
var annotationPoint: CGPoint
// Required views but unused for this implementation.
lazy var leftAccessoryView = UIView()
lazy var rightAccessoryView = UIView()

required init(representedObject: MGLAnnotation, frame: CGRect, annotationPoint: CGPoint) {
    self.representedObject = representedObject
    self.annotatiOnPoint= annotationPoint

    super.init(frame: frame)

    commonInit()
}

required init?(coder aDecoder: NSCoder) {
    let coordinate = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
    self.representedObject = MyAnnotation(coordinate: coordinate, title: "", subtitle: "")
    self.annotatiOnPoint= CGPoint(x: 50.0, y: 50.0)

    super.init(coder: aDecoder)

    commonInit()
}

func commonInit() {
    Bundle.main.loadNibNamed("CustomCalloutView", owner: self, options: nil)
    addSubview(contentView)
}

func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
    // Present the custom callout slightly above the annotation's view. Initially invisble.
    self.center = annotationPoint.applying(CGAffineTransform(translationX: 0.0, y: -120.0))

    view.addSubview(self)
}

func dismissCallout(animated: Bool) {
    removeFromSuperview()
}

}

这与xib文件相关联/标识。现在它只包含一个简单的图像形状。我不得不(重新)引入contentView IBOutlet,因为我无法从Bundle中加载内容并将其添加到commonInit()中的self上使一切变得很愉快。

自定义注释类。

    import UIKit
    import Mapbox

    // MGLAnnotation protocol reimplementation
    class MyAnnotation: NSObject, MGLAnnotation {

    // As a reimplementation of the MGLAnnotation protocol, we have to add mutable coordinate and (sub)title properties ourselves.
    var coordinate: CLLocationCoordinate2D
    var title: String?
    var subtitle: String?

    // Custom properties that we will use to customize the annotation.
    var image: UIImage?
    var reuseIdentifier: String?

    init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String?) {
        self.coordinate = coordinate
        self.title = title
        self.subtitle = subtitle

        self.reuseIdentifier = "myAnnotation"
    }
}

MGLAnnotationView子类。

    import UIKit
    import Mapbox

    class MyAnnotationView: MGLAnnotationView {

    init(reuseIdentifier: String, size: CGSize, annotation: MGLAnnotation) {
        super.init(reuseIdentifier: reuseIdentifier)

        // This property prevents the annotation from changing size when the map is tilted.
        scalesWithViewingDistance = false

        // Begin setting up the view.
        frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        let imageView = UIImageView(frame: frame)
        var image = UIImage()
        if annotation is MyAnnotation {
            image = UIImage(named: "frog")!
        }

        imageView.image = image
        addSubview(imageView)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

自然地,有很多硬编码的数字,并且需要一个名为frog的图像,但是您可以更改所有这些图像并根据需要对其进行改进。需要在身份检查器等中以通常的方式链接CustomCalloutView.swift和CustomCalloutView.xib。



1> Magnas..:

当我为Mapbox注释实现自定义标注时,我使用了xib文件来设计实际的标注。我发现,与尝试从代码中构造UI相比,它给我提供了更多的即时反馈(但显然可以随您的喜好进行操作)。

这给了我类似以下内容。

使用UIImage作为背景,可以实现我选择的任何形状。在这里,我使用白色周围的透明度为您提供问题中提到的圆形元素和底部三角形。

此UIView的Swift文件(您的SpeechBubble)需要符合MGLCalloutView协议,而不是MGLMapViewDelegate您当前所拥有的。您ViewControllerMGLMapViewDelegate,而不是您的自定义标注。在IB中的Identity Inspector中以通常的方式将xib文件和Swift文件配对。所以会是这样的:

    import UIKit
    import Mapbox

    class SpeechBubble: UIView, MGLCalloutView {

    // Your IBOutlets //
    @IBOutlet var contentView: UIView! // The custom callout's view.

    var representedObject: MGLAnnotation
    var annotationPoint: CGPoint
    // Required views but unused for this implementation.
    lazy var leftAccessoryView = UIView()
    lazy var rightAccessoryView = UIView()

    weak var delegate: MGLCalloutViewDelegate?

    // MARK: - init methods

    required init(annotation: YourAnnotation, frame: CGRect, annotationPoint: CGPoint) {
        self.representedObject = annotation
        self.annotatiOnPoint= annotationPoint

        super.init(frame: frame)

        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonInit()
    }

    private func commonInit() {
        Bundle.main.loadNibNamed("SpeechBubble", owner: self, options: nil)
        addSubview(contentView)
        contentView.frame = self.bounds

        // Do your initialisation //
    }

    // MARK: - MGLCalloutView methods

    func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
        // Present the custom callout slightly above the annotation's view. Initially invisble.
        self.center = annotationPoint.applying(CGAffineTransform(translationX: 0, y: -self.frame.height - 20.0))

        // I have logic here for setting the correct image and button states //
    }

    func dismissCallout(animated: Bool) {
        removeFromSuperview()
    }

然后,您似乎只是缺少了在请求时MGLMapViewDelegate实际返回SpeechBubble视图的方法。它应该在您的ViewController文件中。

    func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
        // Do your annotation-specific preparation here //

        // I get the correct size from my xib file.
        let viewFrame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 261.0, height: 168.0))
        // Get the annotation's location in the view's coordinate system.
        let annotatiOnPoint= mapView.convert(annotation.coordinate, toPointTo: nil)
        let customCalloutView = SpeechBubble(annotation: YourAnnotation, frame: viewFrame, annotationPoint: annotationPoint)

        return customCalloutView
    }

希望这将使您更接近实现自己的目标。顺便说一句,这个问题的版本比第一个要领先几英里。

编辑+++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++

在没有看到您的项目的情况下几乎不可能完成这项工作,因此我整理了一个基本的实现。它基于此处的Mapbox示例:Mapbox自定义标注,由于某种原因,它没有显示如何实际提供标注视图。我还扩展了它以允许自定义注释图像。如果可以进行此工作,则应该能够将相关部分移至您自己的项目中。

我强烈建议您,如果尝试实施以下内容,请在一个新项目中进行。

视图控制器。

    import Mapbox

    class ViewController: UIViewController, MGLMapViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let mapView = MGLMapView(frame: view.bounds, styleURL: MGLStyle.lightStyleURL)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.tintColor = .darkGray
        view.addSubview(mapView)

        // Set the map view‘s delegate property.
        mapView.delegate = self

        // Initialize and add the marker annotation.
        let coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 0)
        let marker = MyAnnotation(coordinate: coordinate, title: "Bingo", subtitle: "Bongo")

        // Add marker to the map.
        mapView.addAnnotation(marker)
    }

    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
        return true
    }

    func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
        // Instantiate and return our custom callout view.
        let annotatiOnPoint= mapView.convert(annotation.coordinate, toPointTo: nil)
        let viewFrame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 250.0, height: 178.0))
        return CustomCalloutView(representedObject: annotation, frame: viewFrame, annotationPoint: annotationPoint)
    }

    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
        if let annotatiOnView= mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotationView") {
            return annotationView
        } else {
            let annotatiOnView= MyAnnotationView(reuseIdentifier: "myAnnotationView", size: CGSize(width: 45, height: 45), annotation: annotation)
            return annotationView
        }
    }

    func mapView(_ mapView: MGLMapView, tapOnCalloutFor annotation: MGLAnnotation) {
        // Optionally handle taps on the callout.
        print("Tapped the callout for: \(annotation)")

        // Hide the callout.
        mapView.deselectAnnotation(annotation, animated: true)
    }
}

CustomCalloutView.swift

import UIKit
import Mapbox

class CustomCalloutView: UIView, MGLCalloutView {

@IBOutlet var contentView: UIView!

weak var delegate: MGLCalloutViewDelegate?

var representedObject: MGLAnnotation
var annotationPoint: CGPoint
// Required views but unused for this implementation.
lazy var leftAccessoryView = UIView()
lazy var rightAccessoryView = UIView()

required init(representedObject: MGLAnnotation, frame: CGRect, annotationPoint: CGPoint) {
    self.representedObject = representedObject
    self.annotatiOnPoint= annotationPoint

    super.init(frame: frame)

    commonInit()
}

required init?(coder aDecoder: NSCoder) {
    let coordinate = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
    self.representedObject = MyAnnotation(coordinate: coordinate, title: "", subtitle: "")
    self.annotatiOnPoint= CGPoint(x: 50.0, y: 50.0)

    super.init(coder: aDecoder)

    commonInit()
}

func commonInit() {
    Bundle.main.loadNibNamed("CustomCalloutView", owner: self, options: nil)
    addSubview(contentView)
}

func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
    // Present the custom callout slightly above the annotation's view. Initially invisble.
    self.center = annotationPoint.applying(CGAffineTransform(translationX: 0.0, y: -120.0))

    view.addSubview(self)
}

func dismissCallout(animated: Bool) {
    removeFromSuperview()
}

}

这与xib文件相关联/标识。现在它只包含一个简单的图像形状。我不得不(重新)引入contentView IBOutlet,因为我无法从Bundle中加载内容并将其添加到commonInit()中的self上使一切变得很愉快。

自定义注释类。

    import UIKit
    import Mapbox

    // MGLAnnotation protocol reimplementation
    class MyAnnotation: NSObject, MGLAnnotation {

    // As a reimplementation of the MGLAnnotation protocol, we have to add mutable coordinate and (sub)title properties ourselves.
    var coordinate: CLLocationCoordinate2D
    var title: String?
    var subtitle: String?

    // Custom properties that we will use to customize the annotation.
    var image: UIImage?
    var reuseIdentifier: String?

    init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String?) {
        self.coordinate = coordinate
        self.title = title
        self.subtitle = subtitle

        self.reuseIdentifier = "myAnnotation"
    }
}

MGLAnnotationView子类。

    import UIKit
    import Mapbox

    class MyAnnotationView: MGLAnnotationView {

    init(reuseIdentifier: String, size: CGSize, annotation: MGLAnnotation) {
        super.init(reuseIdentifier: reuseIdentifier)

        // This property prevents the annotation from changing size when the map is tilted.
        scalesWithViewingDistance = false

        // Begin setting up the view.
        frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        let imageView = UIImageView(frame: frame)
        var image = UIImage()
        if annotation is MyAnnotation {
            image = UIImage(named: "frog")!
        }

        imageView.image = image
        addSubview(imageView)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

自然地,有很多硬编码的数字,并且需要一个名为frog的图像,但是您可以更改所有这些图像并根据需要对其进行改进。需要在身份检查器等中以通常的方式链接CustomCalloutView.swift和CustomCalloutView.xib。


推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文介绍了如何在方法参数中指定一个对象的协议,以及如何调用符合该协议的方法。以一个具体的示例说明了如何在方法参数中指定一个UIView子类对象,并且该对象需要符合PixelUI协议,同时方法需要能够访问该对象的属性。 ... [详细]
author-avatar
sprite_77
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有