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
iOS Swift MapKit Custom Annotation
提问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
CustomPointAnnotation
class.We will subclass the
MKPointAnnotation
to set image and assign it on the delegate protocol methodviewForAnnotation
.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
viewForAnnotation
method which is anMKMapViewDelegate
protocol method which gets called for pins to display on the map.viewForAnnotation
protocol 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
CustomPointAnnotation
class in order to access the image name of the pin.We will create a new image set in
Assets.xcassets
and 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。
NSLocationAlwaysUsageDescription
and NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
和 NSLocationWhenInUseUsageDescription
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 MKMapView
displays an annotation view from an annotation. There are
首先,有必要了解如何MKMapView
从注释中显示注释视图。有
- MKMapView - displays the map and manages annotations.
- MKMapViewDelegate - you return data from specific functions to MKMapView.
- MKAnnotation - contains data about a location on the map.
- MKAnnotationView - displays an annotation.
- MKMapView - 显示地图并管理注释。
- MKMapViewDelegate - 您将数据从特定函数返回到 MKMapView。
- MKAnnotation - 包含有关地图上某个位置的数据。
- MKAnnotationView - 显示注释。
An MKAnnotation
holds 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 MKAnnotationView
for 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
. 在未来的某个时刻,当地图视图准备好显示注释时,它将回调委托并要求它MKAnnotationView
为MKAnnotation
. 委托创建并返回视图,地图视图显示它。您可以在故事板或代码中指定委托,例如mapView.delegate = self
.
Location
地点
Tracking the users location is complicated by:
跟踪用户位置很复杂:
- Permission is needed from the user before location tracking is enabled.
- There is a delay after the user allows tracking, until the user's location is available.
- Location services might not even be enabled.
- 在启用位置跟踪之前需要用户的许可。
- 用户允许跟踪后会有一段延迟,直到用户的位置可用。
- 甚至可能无法启用定位服务。
Your code needs to deal with authorisation by checking CLLocationManager.authorizationStatus
, and implementing CLLocationManagerDelegate
methods.
您的代码需要通过检查CLLocationManager.authorizationStatus
和实现CLLocationManagerDelegate
方法来处理授权。
Note that to use requestWhenInUseAuthorization
requires entry for NSLocationWhenInUseUsageDescription
in Info.plist
请注意,使用requestWhenInUseAuthorization
需要输入NSLocationWhenInUseUsageDescription
inInfo.plist
Example
例子
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.png
could 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.image
is 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 =)
如果这不是问题,请提供有关您已采取的调试步骤的更多信息,以便我们其他人可以更好地理解并帮助您。干杯 =)