Chris Cates
Chris Cates

Reputation: 121

Swift 3 - Understanding MKAnnotationView and mapView() function

There are some serious inconsistencies in answers due to Swift updating it's API spec constantly and not being graceful in it's deprecations... So I've decided to ask this question here.

My current code accounts for updating annotations through an asynchronous function that downloads an image off the internet as a remote URL. It can fetch the UIImage the data datatype no problem. However, I don't quite understand what is happening or the reasoning as to why we need to dequeue and engineer the solution this way.

The question is, why do we need to design our annotation this way, when we can just cast an annotation view to an annotation and not worry about every update?

Please review the following code and enlighten me:

import Foundation
import UIKit
import MapKit
import CoreLocation

class MapView: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    var locationManager:CLLocationManager!
    var firstCenter = false

    var regionRadius: CLLocationDistance = 1000
    var mapView: MKMapView!
    var av:MKPinAnnotationView!

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.white

        mapView = MKMapView(
            frame:
            CGRect(
                x: 0,
                y: 0,
                width: view.frame.width,
                height: view.frame.height
            )
        )

        mapView.mapType = MKMapType.standard
        mapView.isZoomEnabled = true
        mapView.isScrollEnabled = true
        mapView.delegate = self

        view.addSubview(mapView)

        mapView.center = view.center

        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()
        locationManager.requestWhenInUseAuthorization()

        if  CLLocationManager.locationServicesEnabled() {
            print("Looking for locations")
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
        }
    }

    public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let userLocation = locations[0]

        if firstCenter == false {
            centerMapOnLocation(location: userLocation)
            firstCenter = true

            let location2d = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)

            let url = URL(string: "https://graph.facebook.com/" + FB.id + "/picture?width=50&height=50")

            DispatchQueue.global().async {
                let data = try? Data(contentsOf: url!)
                DispatchQueue.main.async {
                    let image = UIImage(data: data!)

                    let a = PinAnnotation()
                    a.title = "Netflix and Chill"
                    a.coordinate = location2d
                    a.image = image

                    self.mapView.addAnnotation(a)
                    self.mapView.showAnnotations(self.mapView.annotations, animated: true)
                }
            }
        }
    }

    public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Error \(error)")
    }

    public func centerMapOnLocation(location: CLLocation) {
        let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,regionRadius * 2.0, regionRadius * 2.0)

        mapView.setRegion(coordinateRegion, animated: true)
    }

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

        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "pin")

        if annotationView != nil {
            annotationView?.annotation = annotation
        } else {
            annotationView = MKAnnotationView(
                annotation: annotation,
                reuseIdentifier: "pin"
            )
        }

        annotationView?.cornerRadius = 25

        let anno = annotation as? PinAnnotation
        annotationView?.image = anno?.image

        return annotationView
    }

}

class PinAnnotation: MKPointAnnotation {
    var image:UIImage!
}

Thank you for your time, Chris

Upvotes: 2

Views: 572

Answers (1)

Leemo
Leemo

Reputation: 26

The reason they designed it this way was for performance reasons. This is a direct quote from the their official documentation.

"For performance reasons, you should generally reuse MKAnnotationView objects in your map views. As annotation views move offscreen, the map view moves them to an internally managed reuse queue. As new annotations move onscreen, and your code is prompted to provide a corresponding annotation view, you should always attempt to dequeue an existing view before creating a new one. Dequeueing saves time and memory during performance-critical operations such as scrolling."

Upvotes: 1

Related Questions