我已经尝试了几个小时。 Mapbox网站上的材料仅显示以下内容:
func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
// Instantiate and return our custom callout view.
return CustomCalloutView(representedObject: annotation)
}
问题在于,对于实现CustomCallout的“ CustomCalloutView”或包含的内容,没有任何详细说明。我知道(我认为)它是一个实现MGLCalloutView的类,但是创建一个正确实现该方法的类并非易事,我遇到了各种各样的错误,尤其是围绕一个函数“ self”-> Self。
很高兴看到一个如何实际实现自定义标注的示例。对于像我这样的简单人来说,Mapbox Git上的所有对话都太复杂了。
答案 0 :(得分:7)
MGLAnnotation
是NSObjectProtocol
,仅要求实现它的类和/或对象具有CLLocationCoordinate2D
。该对象应该是您的数据模型,或者与其非常相关。为简单起见,我继承自NSObject。
CustomAnnotation.swift
import Foundation
import UIKit
import Mapbox
class CustomAnnotation: NSObject, MGLAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var image: UIImage
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, image: UIImage) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.image = image
}
}
您的自定义标注视图(MGLCalloutView
)是从NSObject
继承的任何类或对象都可以遵循的协议,并具有以下必需属性,请注意,我继承了UIView的子类NSObject:
class CustomCallOutView: UIView, MGLCalloutView {
var representedObject: MGLAnnotation
// Required views but unused for now, they can just relax
lazy var leftAccessoryView = UIView()
lazy var rightAccessoryView = UIView()
var delegate: MGLCalloutViewDelegate?
required init(annotation: MGLAnnotation) {
self.representedObject = annotation
super.init()
}
func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
}
func dismissCallout(animated: Bool) {
}
}
请注意,require init(annotation:)
有点误导,因为人们期望annotation
是一个对象,而是符合MGLAnnotation
的对象,因此我们可以将其更改为自己的MGLAnnotation数据模型版本。
required init(annotation: CustomAnnotation) {
self.representedObject = annotation
super.init()
}
现在,在MGLCalloutViewDelegate
委托方法presentCallout(rect:view:constrainedRect:)
中,我们将自定义标注(自身)添加到mapView中,并将其作为视图传递给委托函数。我们还希望在关闭后从超级视图中删除该视图:
func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
view.addSubview(self)
}
func dismissCallout(animated: Bool) {
if (animated){
//do something cool
removeFromSuperview()
} else {
removeFromSuperview()
}
}
最后,在您的mapView(_: calloutViewFor annotation:)
方法中,从符合MGLAnnotation
的类或对象中创建一个新的自定义注释,并将其传递到您的自定义标注视图:
func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
let title = annotation.title ?? nil
let subtitle = annotation.subtitle ?? nil
let image = UIImage(named: "apple.png")!
let customAnnotation = CustomAnnotation(coordinate: annotation.coordinate, title: title ?? "no title", subtitle: subtitle ?? "no subtitle", image: image)
return CustomCalloutView(annotation: customAnnotation)
}
作为参考,这是我完整实施的其余部分:
CustomAnnotation.swift
见上文
ViewController.swift
import UIKit
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
lazy var mapView: MGLMapView = {
let mv = MGLMapView(frame: self.view.bounds, styleURL: URL(string: "mapbox://styles/mapbox/streets-v10"))
mv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mv.setCenter(CLLocationCoordinate2D(latitude: 40.7326808, longitude: -73.9843407), zoomLevel: 9, animated: false)
return mv
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
setup()
// Declare the marker `hello` and set its coordinates, title, and subtitle.
let hello = MGLPointAnnotation()
hello.coordinate = CLLocationCoordinate2D(latitude: 40.7326808, longitude: -73.9843407)
hello.title = "Hello world!"
hello.subtitle = "Welcome to my marker"
// Add marker `hello` to the map.
mapView.addAnnotation(hello)
}
func setup() {
self.view.addSubview(mapView)
mapView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Use the default marker. See also: our view annotation or custom marker examples.
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
return nil
}
// Allow callout view to appear when an annotation is tapped.
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
let title = annotation.title ?? nil
let subtitle = annotation.subtitle ?? nil
let image = UIImage(named: "apple.png")!
let customAnnotation = CustomAnnotation(coordinate: annotation.coordinate, title: title ?? "no title", subtitle: subtitle ?? "no subtitle", image: image)
return CustomCalloutView(annotation: customAnnotation)
}
}
CustomCalloutView
import Foundation
import Mapbox
class CustomCalloutView: UIView, MGLCalloutView {
var representedObject: MGLAnnotation
// Required views but unused for now, they can just relax
lazy var leftAccessoryView = UIView()
lazy var rightAccessoryView = UIView()
weak var delegate: MGLCalloutViewDelegate?
//MARK: Subviews -
let titleLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 17.0)
return label
}()
let subtitleLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let imageView:UIImageView = {
let imageview = UIImageView(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
imageview.translatesAutoresizingMaskIntoConstraints = false
imageview.contentMode = .scaleAspectFit
return imageview
}()
required init(annotation: CustomAnnotation) {
self.representedObject = annotation
// init with 75% of width and 120px tall
super.init(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: UIScreen.main.bounds.width * 0.75, height: 120.0)))
self.titleLabel.text = self.representedObject.title ?? ""
self.subtitleLabel.text = self.representedObject.subtitle ?? ""
self.imageView.image = annotation.image
setup()
}
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
// setup this view's properties
self.backgroundColor = UIColor.white
// And their Subviews
self.addSubview(titleLabel)
self.addSubview(subtitleLabel)
self.addSubview(imageView)
// Add Constraints to subviews
let spacing:CGFloat = 8.0
imageView.topAnchor.constraint(equalTo: self.topAnchor, constant: spacing).isActive = true
imageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: spacing).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 52.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 52.0).isActive = true
titleLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: spacing).isActive = true
titleLabel.leftAnchor.constraint(equalTo: self.imageView.rightAnchor, constant: spacing * 2).isActive = true
titleLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -spacing).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
subtitleLabel.topAnchor.constraint(equalTo: self.titleLabel.bottomAnchor, constant: spacing).isActive = true
subtitleLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: spacing).isActive = true
subtitleLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -spacing).isActive = true
subtitleLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
}
func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
//Always, Slightly above center
self.center = view.center.applying(CGAffineTransform(translationX: 0, y: -self.frame.height))
view.addSubview(self)
}
func dismissCallout(animated: Bool) {
if (animated){
//do something cool
removeFromSuperview()
} else {
removeFromSuperview()
}
}
}