web-dev-qa-db-fra.com

iOS Swift Annotation personnalisée MapKit

J'ai du mal à obtenir une annotation personnalisée à charger à l'intérieur de ma vue de carte lorsque j'essaie de placer une épingle.

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

}
}

Informations ajoutées

Je suis certain que la fonctionnalité des boutons fonctionne parfaitement. Avec le code actuel, vidé ci-dessus, l'annotation par défaut de la broche rouge apparaît exactement là où elle devrait. Lorsque je tape sur l'épingle, la description que j'ai spécifiée apparaît également sans problème. Le seul problème que j'ai avec ce code est que je n'arrive pas à faire en sorte que mon image remplace l'ennuyeux pin rouge par défaut

10
Ethan

Je recommande de sous-classer `MKPointAnnotation.

Broche Pokémon

J'ai inclus uniquement le code nécessaire pour afficher une épingle de carte personnalisée. Considérez-le comme un modèle.

Aperçu

  • Nous allons créer un objet d'annotation ponctuelle et attribuer un nom d'image personnalisé à la classe CustomPointAnnotation.

  • Nous allons sous-classer le MKPointAnnotation pour définir l'image et l'assigner sur la méthode du protocole délégué viewForAnnotation.

  • Nous ajouterons une vue d'annotation à la carte après avoir défini les coordonnées de l'annotation de point avec un titre et un sous-titre.

  • Nous allons implémenter la méthode viewForAnnotation qui est une méthode de protocole MKMapViewDelegate qui est appelée pour que les broches s'affichent sur la carte. viewForAnnotation la méthode du protocole est le meilleur endroit pour personnaliser la vue des broches et lui affecter une image personnalisée.

  • Nous allons retirer la file d'attente et retourner une annotation réutilisable pour l'identifiant donné et convertir l'annotation dans notre classe CustomPointAnnotation personnalisée afin d'accéder au nom d'image de la broche.

  • Nous allons créer un nouvel ensemble d'images dans Assets.xcassets et placez [email protected] et [email protected] en conséquence.

  • N'oubliez pas plist.

NSLocationAlwaysUsageDescription et NSLocationWhenInUseUsageDescription

enter image description here

Comme toujours tester sur un vrai appareil.

Le 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
}
}
51
tymac

Il y a quelques problèmes auxquels vous devez faire face.

MKMapView et annotations

Tout d'abord, il est nécessaire de comprendre comment MKMapView affiche une vue d'annotation à partir d'une annotation. Il y a

  1. MKMapView - affiche la carte et gère les annotations.
  2. MKMapViewDelegate - vous renvoyez les données de fonctions spécifiques à MKMapView.
  3. MKAnnotation - contient des données sur un emplacement sur la carte.
  4. MKAnnotationView - affiche une annotation.

Un MKAnnotation contient les données d'un emplacement sur la carte. Vous créez ces données et les remettez à MKMapView. À un certain moment dans le futur, lorsque la vue de carte sera prête à afficher l'annotation, elle rappellera au délégué et lui demandera de créer un MKAnnotationView pour un MKAnnotation. Le délégué crée et renvoie la vue et la vue de la carte l'affiche. Vous spécifiez le délégué dans le storyboard, ou dans le code par exemple mapView.delegate = self.

Emplacement

Le suivi de l'emplacement des utilisateurs est compliqué par:

  1. Une autorisation de l'utilisateur est nécessaire avant que le suivi de localisation ne soit activé.
  2. Il y a un délai après que l'utilisateur autorise le suivi, jusqu'à ce que l'emplacement de l'utilisateur soit disponible.
  3. Les services de localisation peuvent même ne pas être activés.

Votre code doit gérer l'autorisation en vérifiant CLLocationManager.authorizationStatus, et implémentant les méthodes CLLocationManagerDelegate.

Notez que pour utiliser requestWhenInUseAuthorization nécessite une entrée pour NSLocationWhenInUseUsageDescription dans Info.plist

Exemple

Exemple de projet sur 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

    }
}
13
Luke Van In

vérifiez votre image.png dans votre bundle de projet ou Assets.xcassets

 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

        }
5
Bhoomi Jagani

Faites comme suit peut être un travail pour vous.

1) Créez une classe personnalisée pour la broche d'annotation.

class CustomPointAnnotation: MKPointAnnotation {
    var imageName: UIImage!
}

2) Définissez la variable comme ci-dessous.

var locationManager = CLLocationManager()

3) Appelez la méthode ci-dessous dans viewDidLoad()

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

4) Mettez le code ci-dessous dans viewWillAppear()

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

5) Mettre en œuvre la méthode la plus importante ci-dessous.

 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) Exécutez le code ci-dessous où vous avez reçu une image de broche personnalisée

   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)
4
iMHitesh Surani

D'après le code et selon le guide MapKit, votre code semble correct. Je pense que ça pourrait être cette ligne annotationView.image = UIImage(named: "image.png")

Y a-t-il une chance que image.png Puisse être le mauvais nom d'image ou ne pas être ajouté au projet lors de la compilation? Juste pour info, si vous utilisez .xcassets, Vous n'avez pas besoin d'ajouter un .png.

Comme annotationView.image Est facultatif, lorsque l'image UIImage(named: "image.png") est nulle, elle ne se bloquera pas mais restituera simplement l'image de broche par défaut.

Si ce n'est pas le problème, veuillez fournir plus d'informations sur les étapes de débogage que vous avez prises afin que le reste d'entre nous puisse mieux comprendre et vous aider. Acclamations =)

1
Zac Kwan