【问题标题】:iOS Swift MapKit Custom Annotation [closed]iOS Swift MapKit 自定义注释 [关闭]
【发布时间】:2020-07-19 19:02:11
【问题描述】:

当我尝试放置图钉时,无法在地图视图中加载自定义注释。

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

}
}

添加信息

我很肯定按钮功能完美无缺。使用上面转储的当前代码,默认的红色引脚注释出现在它应该出现的位置。当我点击图钉时,我指定的描述也会毫无问题地出现。 我对这段代码的唯一问题是我无法让我的图像代替无聊的默认红色图钉

【问题讨论】:

  • 变量名应以小写字母开头
  • 以及所有其他不以小写字母开头的
  • 除了使您的代码更易于阅读之外,它不会做任何事情。老实说,我什至不知道您有问题,您的问题中从未提及。听起来您想要进行代码审查。
  • @dan ...所以你只看了问题的第一句话吗?
  • 您的问题陈述不清楚。您需要明确说明您的问题是什么,并提供您的问题的a Minimal, Complete, and Verifiable example。不要只是转储你的代码。

标签: ios swift mapkit mapkitannotation


【解决方案1】:

我建议子类化 `MKPointAnnotation。

宝可梦别针

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

大纲

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

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

  • 我们将点注解的坐标设置好后,给地图添加一个注解视图,带有标题和副标题。

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

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

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

  • 别忘了 plist。

NSLocationAlwaysUsageDescription 和 NSLocationWhenInUseUsageDescription

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

调酒?

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

【讨论】:

  • 那么我到底应该把图片的名字放在哪里呢?像“image.png”
  • png 的名称无关紧要,因为“Pokemon Pin”是蓝色 Assets.xcassets 文件夹中图像集的名称。当您选择“Pokemon Pin”图像集时,您将看到 x1 x2 x3 图像的常用占位符。这些 png 可以命名任何东西。图像集的名称很重要,因为它必须匹配 pointAnnotation.pinCustomImageName = "Pokemon Pin"。
  • 上面的代码在 swift 3 中不起作用。在 let customPointAnnotation = annotation as 时崩溃! CustomPointAnnotation ...
  • 改成if let customPointAnnotation = annotation as? CustomPointAnnotation 避免崩溃
  • 没有在地图上显示图钉。
【解决方案2】:

您需要处理一些问题。

MKMapView 和注释

首先要了解MKMapView是如何从注解中显示注解视图的。有

  1. MKMapView - 显示地图并管理注释。
  2. MKMapViewDelegate - 您将数据从特定函数返回到 MKMapView。
  3. MKAnnotation - 包含有关地图上某个位置的数据。
  4. MKAnnotationView - 显示注释。

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

位置

跟踪用户的位置很复杂:

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

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

请注意,要使用requestWhenInUseAuthorization,需要在Info.plist 中输入NSLocationWhenInUseUsageDescription

示例

Example project on 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

    }
}

【讨论】:

    【解决方案3】:

    检查项目包或 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
    }
    

    【讨论】:

    • 嗨!对你有用吗?
    • 在故事板上按下按钮时是否必须调用该函数?
    • 它是mapview的委托方法。它是一个注解(pin)视图的方法。如果你想在按钮点击时添加pin,然后在你的按钮点击中添加addAnnotation代码。
    • 如果你能解决你的问题,你可以接受我的回答。鼓励提供更多帮助。
    • 嗨..这不适合你吗?如果可行,你可以接受我的回答。
    【解决方案4】:

    如下操作可能对你有用。

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

    class CustomPointAnnotation: MKPointAnnotation {
        var imageName: UIImage!
    }
    

    2)如下定义变量。

    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)
    

    【讨论】:

    • let cpa = annotation as! CustomPointAnnotation 是魔法线!
    【解决方案5】:

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

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

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

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

    【讨论】:

    • 我所做的只是将图片复制并粘贴到项目中。我应该做其他事情来加载它吗?我知道图片名称与代码中的参考名称相同。
    • 推荐使用Assets.xcassets。您可以将图像拖入Assets.xcassets 并从那里管理您未来的所有资产。如果你真的不想,至少检查一下图片是否被添加到你的项目Copy Bundle Resources下的Target > Build Phrase > Copy Bundle Resources
    • 好的,我会尽快检查。谢谢,希望这行得通。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    • 2011-12-27
    相关资源
    最近更新 更多