iOS Swift MapKit 自定义注解

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38274115/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 09:39:37  来源:igfitidea点击:

iOS Swift MapKit Custom Annotation

iosswiftmapkitmapkitannotation

提问by Ethan

I am having some trouble getting a custom annotation to load inside of my map view when I try to place a pin.

当我尝试放置图钉时,在我的地图视图中加载自定义注释时遇到了一些问题。

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

}
}

Added Information

添加信息

I am positive that the button functionality works perfect. With the current code, dumped above, the default red pin annotation appears right where it should. When I tap on the pin, the description I specified also appears without an issue. The only problem I am having with this code is that I cannot get my image to take the place of the boring, default red pin

我确信按钮功能完美无缺。使用上面转储的当前代码,默认的红色引脚注释出现在它应该出现的地方。当我点击 pin 时,我指定的描述也不会出现问题。我使用此代码遇到的唯一问题是我无法让我的图像代替无聊的默认红色图钉

回答by Edison

I recommend subclassing `MKPointAnnotation.

我建议子类化`MKPointAnnotation。

Pokémon Pin

神奇宝贝图钉

I have included only the necessary code to display a custom map pin. Think of it as a template.

我只包含了显示自定义地图图钉所需的代码。把它想象成一个模板

Outline

大纲

  • We will create a point annotation object and assigning a custom image name with the CustomPointAnnotationclass.

  • We will subclass the MKPointAnnotationto set image and assign it on the delegate protocol method viewForAnnotation.

  • We will add an annotation view to the map after setting the coordinate of the point annotation with a title and a subtitle.

  • We will implement the viewForAnnotationmethod which is an MKMapViewDelegateprotocol method which gets called for pins to display on the map. viewForAnnotationprotocol method is the best place to customise the pin view and assign a custom image to it.

  • We will dequeue and return a reusable annotation for the given identifier and cast the annotation to our custom CustomPointAnnotationclass in order to access the image name of the pin.

  • We will create a new image set in Assets.xcassetsand place [email protected] and [email protected] accordingly.

  • Don't forget plist.

  • 我们将创建一个点注释对象并为CustomPointAnnotation该类分配一个自定义图像名称。

  • 我们将子类化MKPointAnnotation以设置图像并将其分配给委托协议方法viewForAnnotation

  • 我们将在使用标题和副标题设置点注释的坐标后向地图添加注释视图。

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

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

  • 我们将创建一个新图像集Assets.xcassets并相应地放置 [email protected][email protected]

  • 不要忘记 plist。

NSLocationAlwaysUsageDescriptionand NSLocationWhenInUseUsageDescription

NSLocationAlwaysUsageDescriptionNSLocationWhenInUseUsageDescription

enter image description here

在此处输入图片说明

As always test on a real device.

一如既往地在真实设备上测试。

The swizzle

调酒

//1

//1

CustomPointAnnotation.swift

import UIKit
import MapKit

class CustomPointAnnotation: MKPointAnnotation {
var pinCustomImageName:String!
}

//2

//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
}
}

回答by Luke Van In

There are a few issues you need to deal with.

您需要处理几个问题。

MKMapView and annotations

MKMapView 和注释

Firstly, it is necessary to understand how MKMapViewdisplays an annotation view from an annotation. There are

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

  1. MKMapView - displays the map and manages annotations.
  2. MKMapViewDelegate - you return data from specific functions to MKMapView.
  3. MKAnnotation - contains data about a location on the map.
  4. MKAnnotationView - displays an annotation.
  1. MKMapView - 显示地图并管理注释。
  2. MKMapViewDelegate - 您将数据从特定函数返回到 MKMapView。
  3. MKAnnotation - 包含有关地图上某个位置的数据。
  4. MKAnnotationView - 显示注释。

An MKAnnotationholds the data for a location on the map. You create this data and hand it to MKMapView. At some point in the future, when the map view is ready to display the annotation it will call back to the delegate and ask it to create an MKAnnotationViewfor an MKAnnotation. The delegate creates and returns the view and the map view displays it. You specify the delegate in the storyboard, or in code e.g. mapView.delegate = self.

AnMKAnnotation保存地图上某个位置的数据。您创建此数据并将其交给MKMapView. 在未来的某个时刻,当地图视图准备好显示注释时,它将回调委托并要求它MKAnnotationViewMKAnnotation. 委托创建并返回视图,地图视图显示它。您可以在故事板或代码中指定委托,例如mapView.delegate = self.

Location

地点

Tracking the users location is complicated by:

跟踪用户位置很复杂:

  1. Permission is needed from the user before location tracking is enabled.
  2. There is a delay after the user allows tracking, until the user's location is available.
  3. Location services might not even be enabled.
  1. 在启用位置跟踪之前需要用户的许可。
  2. 用户允许跟踪后会有一段延迟,直到用户的位置可用。
  3. 甚至可能无法启用定位服务。

Your code needs to deal with authorisation by checking CLLocationManager.authorizationStatus, and implementing CLLocationManagerDelegatemethods.

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

Note that to use requestWhenInUseAuthorizationrequires entry for NSLocationWhenInUseUsageDescriptionin Info.plist

请注意,使用requestWhenInUseAuthorization需要输入NSLocationWhenInUseUsageDescriptioninInfo.plist

Example

例子

Example project on GitHub.

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.
    // E.g. 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

    }
}

回答by Bhoomi Jagani

check your image.png in your project bundle or Assets.xcassets

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

 func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

    guard !annotation.isKind(of: MKUserLocation.self) else {
        return nil
    }

    let annotationIdentifier = "AnnotationIdentifier"

    var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: 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
}

回答by COVID19

Do as follow may be work for you.

按照以下操作可能对您有用。

1) Create custom class for the Annotation Pin.

1) 为 Annotation Pin 创建自定义类。

class CustomPointAnnotation: MKPointAnnotation {
    var imageName: UIImage!
}

2)Define variable as below.

2)定义变量如下。

var locationManager = CLLocationManager()

3) Call below method in viewDidLoad()

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

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

4) Put below code in viewWillAppear()

4) 输入以下代码 viewWillAppear()

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

5) Most important implement below method.

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) Execute below code where you you have received custom pin image

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)

回答by Zac Kwan

From the code and according to the MapKit guide, your code look correct. I am thinking that it could be this line annotationView.image = UIImage(named: "image.png")

根据代码和 MapKit 指南,您的代码看起来是正确的。我想这可能是这条线annotationView.image = UIImage(named: "image.png")

Is there a chance that image.pngcould be the wrong image name or not added in to the project when compile? Also just fyi, if you are using .xcassets, you does not have to add a .png.

image.png编译时是否有可能是错误的图像名称或未添加到项目中?也仅供参考,如果您使用的是.xcassets,则不必添加.png.

As annotationView.imageis a optional, when the image UIImage(named: "image.png")is nil, it will not crash but just render the default pin image.

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

If this is not the issue, please provide more info on the debugging steps that you have taken so the rest of us can understand better and help you. Cheers =)

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