iOS Swift MapKit自定义注释

我尝试放置图钉时遇到了一些麻烦,导致在我的地图视图中加载自定义注释。

import UIKit import MapKit import CoreLocation class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate{ @IBAction func ReportBtn(sender: AnyObject) { //MARK: Report Date And Time Details let ReportTime = NSDate() let TimeStamp = NSDateFormatter() TimeStamp.timeStyle = NSDateFormatterStyle.ShortStyle TimeStamp.dateStyle = NSDateFormatterStyle.ShortStyle TimeStamp.stringFromDate(ReportTime) //MARK: Default Point Annotation Begins let ReportAnnotation = MKPointAnnotation() ReportAnnotation.title = "Annotation Created" ReportAnnotation.subtitle = ReportTime.description ReportAnnotation.coordinate = locationManager.location!.coordinate mapView(MainMap, viewForAnnotation: ReportAnnotation) MainMap.addAnnotation(ReportAnnotation) } @IBOutlet weak var MainMap: MKMapView! let locationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() self.locationManager.requestWhenInUseAuthorization() self.locationManager.delegate = self self.locationManager.desiredAccuracy = kCLLocationAccuracyBest self.locationManager.startUpdatingLocation() self.MainMap.showsUserLocation = true } //MARK: - Location Delegate Methods func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { let location = locations.last let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude) let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02 )) self.MainMap.setRegion(region, animated: true) //self.locationManager.stopUpdatingLocation() } func locationManager(manager: CLLocationManager, didFailWithError error: NSError){ print(error.localizedDescription) } //MARK:Custom Annotation Begins Here func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { guard !annotation.isKindOfClass(MKUserLocation) else { return nil } /*if annotation.isKindOfClass(MKUserLocation){ //emty return, guard wasn't cooperating }else{ return nil }*/ let annotationIdentifier = "AnnotationIdentifier" var annotationView: MKAnnotationView? if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier){ annotationView = dequeuedAnnotationView annotationView?.annotation = annotation } else{ let av = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier) av.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure) annotationView = av } if let annotationView = annotationView { annotationView.canShowCallout = true annotationView.image = UIImage(named: "image.png") } return annotationView } } 

新增信息

我肯定buttonfunction是完美的。 使用当前的代码,在上面倾倒,默认的红色针脚注释出现在它应该在的地方。 当我点击销,我指定的描述也出现没有问题。 我使用这个代码的唯一问题是,我无法让我的图像取代枯燥的默认红色引脚

我build议inheritance`MKPointAnnotation。

神奇宝贝

我只包括必要的代码来显示一个自定义的地图引脚。 把它看作一个模板

大纲

  • 我们将创build一个点注释对象,并使用CustomPointAnnotation类指定一个自定义图像名称。

  • 我们将MKPointAnnotation来设置图像,并将其分配给委托协议方法viewForAnnotation

  • 设置点标注的坐标后,我们将添加一个标注视图到标题和标题。

  • 我们将实现viewForAnnotation方法,它是一个MKMapViewDelegate协议方法,被调用引脚在地图上显示。 viewForAnnotation协议方法是自定义针视图并为其分配自定义图像的最佳位置。

  • 我们将出列并返回给定标识符的可重用注释,并将注释转换为我们自定义的CustomPointAnnotation类,以便访问引脚的图像名称。

  • 我们将在Assets.xcassets创build一个新的图像集,并相应地放置image@3x.png和image@2x.png。

  • 别忘了plist。

NSLocationAlwaysUsageDescriptionNSLocationWhenInUseUsageDescription

在这里输入图像说明

一如往常在真实的设备上testing。

这个swizzle🌀

// 1

 CustomPointAnnotation.swift import UIKit import MapKit class CustomPointAnnotation: MKPointAnnotation { var pinCustomImageName:String! } 

// 2

 ViewController.swift import UIKit import MapKit class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate { @IBOutlet weak var pokemonMap: MKMapView! let locationManager = CLLocationManager() var pointAnnotation:CustomPointAnnotation! var pinAnnotationView:MKPinAnnotationView! override func viewDidLoad() { super.viewDidLoad() //Mark: - Authorization locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.requestAlwaysAuthorization() locationManager.startUpdatingLocation() pokemonMap.delegate = self pokemonMap.mapType = MKMapType.Standard pokemonMap.showsUserLocation = true } func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { let location = CLLocationCoordinate2D(latitude: 35.689949, longitude: 139.697576) let center = location let region = MKCoordinateRegionMake(center, MKCoordinateSpan(latitudeDelta: 0.025, longitudeDelta: 0.025)) pokemonMap.setRegion(region, animated: true) pointAnnotation = CustomPointAnnotation() pointAnnotation.pinCustomImageName = "Pokemon Pin" pointAnnotation.coordinate = location pointAnnotation.title = "POKéSTOP" pointAnnotation.subtitle = "Pick up some Poké Balls" pinAnnotationView = MKPinAnnotationView(annotation: pointAnnotation, reuseIdentifier: "pin") pokemonMap.addAnnotation(pinAnnotationView.annotation!) } func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { print(error.localizedDescription) } //MARK: - Custom Annotation func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { let reuseIdentifier = "pin" var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseIdentifier) if annotationView == nil { annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier) annotationView?.canShowCallout = true } else { annotationView?.annotation = annotation } let customPointAnnotation = annotation as! CustomPointAnnotation annotationView?.image = UIImage(named: customPointAnnotation.pinCustomImageName) return annotationView } } 

有几个问题需要处理。

MKMapView和注释

首先,有必要了解MKMapView如何从注释中显示注释视图。 有

  1. MKMapView – 显示地图并pipe理注释。
  2. MKMapViewDelegate – 你从特定的函数返回数据到MKMapView。
  3. MKAnnotation – 包含有关地图上位置的数据。
  4. MKAnnotationView – 显示注释。

MKAnnotation包含地图上位置的数据。 你创build这个数据并把它交给MKMapView 。 在将来的某个时刻,当地图视图准备好显示注释时,它将callback委托并要求它为MKAnnotationView创build一个MKAnnotation 。 委托创build并返回视图,地图视图显示它。 您可以在故事板或代码中指定委托,例如mapView.delegate = self

位置

跟踪用户的位置很复杂:

  1. 在启用位置跟踪之前,用户需要权限。
  2. 在用户允许跟踪之后有一段延迟,直到用户的位置可用。
  3. 位置服务甚至可能不被启用。

你的代码需要通过检查CLLocationManager.authorizationStatus和实现CLLocationManagerDelegate方法来处理授权。

请注意,要使用requestWhenInUseAuthorization需要inputNSLocationWhenInUseUsageDescription in Info.plist

GitHub上的示例项目 。

 import UIKit import MapKit import CoreLocation class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate { // Instance of location manager. // This is is created in viewDidLoad() if location services are available. var locationManager: CLLocationManager? // Last location made available CoreLocation. var currentLocation: MKUserLocation? { didSet { // Hide the button if no location is available. button.hidden = (currentLocation == nil) } } // Date formatter for formatting dates in annotations. // We use a lazy instance so that it is only created when needed. lazy var formatter: NSDateFormatter = { let formatter = NSDateFormatter() formatter.timeStyle = NSDateFormatterStyle.ShortStyle formatter.dateStyle = NSDateFormatterStyle.ShortStyle return formatter }() @IBOutlet var button: UIButton! @IBOutlet var mapView: MKMapView! override func viewDidLoad() { super.viewDidLoad() mapView.delegate = self // Track the user's location if location services are enabled. if CLLocationManager.locationServicesEnabled() { locationManager = CLLocationManager() locationManager?.delegate = self locationManager?.desiredAccuracy = kCLLocationAccuracyBest switch CLLocationManager.authorizationStatus() { case .AuthorizedAlways, .AuthorizedWhenInUse: // Location services authorised. // Start tracking the user. locationManager?.startUpdatingLocation() mapView.showsUserLocation = true default: // Request access for location services. // This will call didChangeAuthorizationStatus on completion. locationManager?.requestWhenInUseAuthorization() } } } // // CLLocationManagerDelegate method // Called by CLLocationManager when access to authorisation changes. // func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { switch status { case .AuthorizedAlways, .AuthorizedWhenInUse: // Location services are authorised, track the user. locationManager?.startUpdatingLocation() mapView.showsUserLocation = true case .Denied, .Restricted: // Location services not authorised, stop tracking the user. locationManager?.stopUpdatingLocation() mapView.showsUserLocation = false currentLocation = nil default: // Location services pending authorisation. // Alert requesting access is visible at this point. currentLocation = nil } } // // MKMapViewDelegate method // Called when MKMapView updates the user's location. // func mapView(mapView: MKMapView, didUpdateUserLocation userLocation: MKUserLocation) { currentLocation = userLocation } @IBAction func addButtonTapped(sender: AnyObject) { guard let coordinate = currentLocation?.coordinate else { return } let reportTime = NSDate() let formattedTime = formatter.stringFromDate(reportTime) let annotation = MKPointAnnotation() annotation.title = "Annotation Created" annotation.subtitle = formattedTime annotation.coordinate = coordinate mapView.addAnnotation(annotation) } // // From Bhoomi's answer. // // MKMapViewDelegate method // Called when the map view needs to display the annotation. // Eg If you drag the map so that the annotation goes offscreen, the annotation view will be recycled. When you drag the annotation back on screen this method will be called again to recreate the view for the annotation. // func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { guard !annotation.isKindOfClass(MKUserLocation) else { return nil } let annotationIdentifier = "AnnotationIdentifier" var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier) if annotationView == nil { annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier) annotationView!.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure) annotationView!.canShowCallout = true } else { annotationView!.annotation = annotation } annotationView!.image = UIImage(named: "smile") return annotationView } } 

做下面的事情可能会为你工作。

1)为注释引脚创build自定义类。

 class CustomPointAnnotation: MKPointAnnotation { var imageName: UIImage! } 

2)如下定义variables。

 var locationManager = CLLocationManager() 

3)在viewDidLoad()方法中调用下面的方法

  func checkLocationAuthorizationStatus() { if CLLocationManager.authorizationStatus() == .AuthorizedAlways { map.showsUserLocation = false } else { locationManager.requestWhenInUseAuthorization() } } 

4)将下面的代码放在viewWillAppear()

  self.map.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.requestAlwaysAuthorization() locationManager.delegate = self dispatch_async(dispatch_get_main_queue(),{ self.locationManager.startUpdatingLocation() }) 

5)下面的方法最重要的工具。

  func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { if !(annotation is CustomPointAnnotation) { return nil } let reuseId = "Location" var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) if anView == nil { anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId) anView!.canShowCallout = true } else { anView!.annotation = annotation } let cpa = annotation as! CustomPointAnnotation anView!.image = cpa.imageName return anView } 

6)执行下面的代码,你已经收到定制引脚图像

  let strLat = "YOUR LATITUDE" let strLon = "YOUR LONGITUDE" let info = CustomPointAnnotation() info.coordinate = CLLocationCoordinate2DMake(strLat.toDouble()!,strLon.toDouble()!) info.imageName = resizedImage info.title = dict!["locationName"]! as? String self.map.addAnnotation(info) 

在您的项目包或Assets.xcassets中检查image.png

  func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { guard !annotation.isKindOfClass(MKUserLocation) else { return nil } let annotationIdentifier = "AnnotationIdentifier" var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier) if annotationView == nil { annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier) annotationView.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure) annotationView!.canShowCallout = true } else { annotationView!.annotation = annotation } annotationView!.image = UIImage(named: "image.png") return annotationView } 

从代码和根据MapKit指南,你的代码看起来是正确的。 我在想,它可能是这一行annotationView.image = UIImage(named: "image.png")

有没有image.png可能是错误的图像名称或编译时不添加到项目中的机会? 另外只是fyi,如果你使用.xcassets ,你不必添加一个.png

由于annotationView.image是可选的,当图像UIImage(named: "image.png")为零时,它不会崩溃,而只是渲染默认的图像。

如果这不是问题,请提供更多关于您所采取的debugging步骤的信息,以便我们其他人更好地理解并帮助您。 干杯=)