现在,我的应用程序显示一个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
您当前所拥有的。您ViewController
是MGLMapViewDelegate
,而不是您的自定义标注。在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。
当我为Mapbox注释实现自定义标注时,我使用了xib文件来设计实际的标注。我发现,与尝试从代码中构造UI相比,它给我提供了更多的即时反馈(但显然可以随您的喜好进行操作)。
这给了我类似以下内容。
使用UIImage作为背景,可以实现我选择的任何形状。在这里,我使用白色周围的透明度为您提供问题中提到的圆形元素和底部三角形。
此UIView的Swift文件(您的SpeechBubble)需要符合MGLCalloutView
协议,而不是MGLMapViewDelegate
您当前所拥有的。您ViewController
是MGLMapViewDelegate
,而不是您的自定义标注。在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。