Когда я реализовал пользовательскую выноску для своих аннотаций Mapbox, я использовал файл xib для разработки фактической выноски.Я считаю, что это дает мне гораздо больше мгновенной обратной связи, чем попытка вызвать пользовательский интерфейс из кода (но, очевидно, делать то, что вы предпочитаете).
Что дает мне что-то вроде следующего.
Использование UIImage для фона позволяет мне получить любую форму, которую я выберу.Здесь я использую прозрачность вокруг белого, чтобы получить круговые элементы и нижний треугольник, который вы упомянули в своем вопросе.
Файл Swift для этого UIView (ваш SpeechBubble) должен соответствовать протоколу MGLCalloutView
, а не MGLMapViewDelegate
как у вас сейчас.Ваш ViewController
- это MGLMapViewDelegate
, а не ваш пользовательский выноска.Соедините файл xib и файл Swift обычным способом в Identity Inspector в IB.Итак, было бы что-то вроде этого:
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 Custom Callout , который по какой-то причине не показывает, как на самом деле предоставить представление выноски.Я также расширил его, чтобы учесть пользовательское изображение аннотации.Если вы можете заставить это работать, вы сможете перемещать соответствующие части в свой собственный проект.
Я настоятельно рекомендую вам, если вы попытаетесь реализовать описанные ниже вещи, сделать это в новом проекте.
Контроллер представления.
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.Это просто содержит простую форму изображения на данный момент.Мне пришлось (заново) представить IBOutlet contentView, так как у меня были проблемы с загрузкой вещей из Bundle и добавлением их в self в commonInit (), которые сделали все счастливым.
Пользовательский класс аннотаций.
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")
}
}
Естественно, естьмного жестко закодированных чисел и требование для изображения, называемого лягушкой, но вы можете изменить все это и улучшить его, как вы хотите.CustomCalloutView.swift и CustomCalloutView.xib должны быть связаны обычным способом в инспекторе идентификации и т. Д.