MapKit: Two Annotations Animations

The cartography series

Subscribe to my newsletter and never miss my upcoming articles

Hello ladies and gentlemen, Leo here.

Today we'll do some explorations in MapKit. If you are into MapKit, a huge fan of maps, a cartographer (never knows) or just want to know how to auto resize a map base in two annotations, this post is for you. MapKit is a VERY versatile framework embed in iOS that provide us with a lot of API that help the development using maps. Apple documentation define as "Display map or satellite imagery within your app, call out points of interest, and determine placemark information for map coordinates."

The painting from this post is The Geographer (Dutch: De geograaf) is a painting created by Dutch artist Johannes Vermeer in 1668–1669. As we talking about maps, it's the best painting to represent that, a geographer.

No more talking, let's code!

Problem

Imagine that you work for a food company and now you have to track where the delivery person in real-time for the user knows how long will take to the hamburger/salad/whatever get to his house.

The objective is to do something like this:

The left pin is the delivery person and the right one is the delivery address.

  1. First create a plain simple app.

Screen Shot 2021-03-17 at 08
![Screen Shot 2021-03-17 at 09.51.51.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1615985515170/KU_PMM9ATZ.png)
47.59.png

Make sure that you choose language Swift as language and Storyboard as interface. We won't use storyboards and it's just a example project so I'll not show how to cleanup all the storyboards extra configs because it will not do any harm now.

  1. Create a new file (shortcut command+n) called DeliveryMapView

Screen Shot 2021-03-17 at 08.52.33.png

Inside that new file, copy this lines:

    private var mapView: MKMapView!
    private var deliveryPersonAnnotation = MKPointAnnotation()
    private var deliveryAddressAnnotation = MKPointAnnotation()

This will create a implicit unwrapped optional mapView and two instances of MKPointAnnotation, the deliveryPersonAnnotation and the deliveryAddressAnnotation. The deliveryAddressAnnotation will be the pin at your home and the deliveryPersonAnnotation will be the moving pin in the map ( the delivery person coming closer and closer).

Continue in the DeliveryMapView file and copy/paste this:

    init() {
        super.init(frame: .zero)
        configureView()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func configureView() {
        configureMKMapView() // 1
        configureDeliveryAddressAnnotation() // 2
    }

The mark 1 and mark 2 are broken but don't worry we'll fix that in a moment. It's just standard configurations for the view. Let's create the configureMKMapView func:

    private func configureMKMapView() {

        // mark 1
        mapView = MKMapView() 
        addSubview(mapView) 
        mapView.translatesAutoresizingMaskIntoConstraints = false 

        // mark 2
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOpacity = 1
        layer.shadowOffset = CGSize(width: 0, height: 10)
        layer.shadowRadius = 5
        layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.2).cgColor
        isUserInteractionEnabled = false
        mapView.layer.cornerRadius = 10

        // mark 3
        NSLayoutConstraint.activate([
            mapView.topAnchor.constraint(equalTo: topAnchor),
            mapView.leadingAnchor.constraint(equalTo: leadingAnchor),
            mapView.trailingAnchor.constraint(equalTo: trailingAnchor),
            mapView.bottomAnchor.constraint(equalTo: bottomAnchor),
            mapView.heightAnchor.constraint(equalToConstant: 300),
            mapView.widthAnchor.constraint(equalToConstant: 300)
        ])
    }

On mark 1 we are just creating a instance of MKMapView, add that as a subview to the view and putting tamic ( translatesAutoresizingMaskIntoConstraints) to false so we can use auto layout. At mark 2 just a plain shadow configuration, nothing fancy ( it will be better to draw shadows with shadow path but it's just a fast example). And lastly the mark 3 we set the size and the constraints of the view with auto layout.

Now creating the func configureDeliveryAddressAnnotation:

    private func configureDeliveryAddressAnnotation() {
        deliveryAddressAnnotation.coordinate = CLLocationCoordinate2D(latitude: -23.6944, longitude: -46.5654) // mark 1
        mapView.addAnnotation(deliveryAddressAnnotation) // mark 2
    }

Now the magic begins, when you set the coordinate attribute from deliveryAddressAnnotation you are telling to that pin the exact location of it in the world. This means that you can add any amount of pins in the map just adding (mark 2) an annotation in the MKMapView.

  1. Show me the map!

Back to the ViewController.swift file, add this:

class ViewController: UIViewController {

    private var deliveryMapView: DeliveryMapView!
    private var latitude = -23.6944
    private var longitude = -47.1154
    private var timer: Timer!

    override func viewDidLoad() {
        super.viewDidLoad()
        configureView()
        view.backgroundColor = .white
    }

    private func configureView() {
        deliveryMapView = DeliveryMapView()
        view.addSubview(deliveryMapView)
        deliveryMapView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            deliveryMapView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            deliveryMapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 150)
        ])
    }

Build, run and you should see this:

Screen Shot 2021-03-17 at 09.52.52.png

  1. Add the second pin

Let's head back to the DeliveryMapView.swift and add this function:

    func updateDeliveryPersonPosition(deliveryPersonLatitude: Double, deliveryPersonLongitude: Double) {

            deliveryPersonAnnotation.coordinate = CLLocationCoordinate2D(latitude: deliveryPersonLatitude, longitude: deliveryPersonLongitude)
            mapView.showAnnotations([deliveryAddressAnnotation,deliveryPersonAnnotation], animated: true)
    }

The showAnnotations api from MapKit do the trick here. You can automatically zoom any number of pins in the map. This is very handy when you need to show two points at the same time and one of them will be moving around so the map zoom should follow accordingly.

And call it from ViewController.swift :

    private func configureView() {

       [...]

        deliveryMapView.updateDeliveryPersonPosition(deliveryPersonLatitude: latitude, deliveryPersonLongitude: longitude) // this 

    }

You should see this:

Screen Shot 2021-03-17 at 10.01.54.png

Great, you already have two pins inside your map. We just need to test, let's fake a deliveryPersonMoviment in the map using Timer in the ViewController.swift:

    private func configureView() {

       [...]

        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [self] timer in
            deliveryMapView.updateDeliveryPersonPosition(deliveryPersonLatitude: latitude, deliveryPersonLongitude: longitude)
            latitude += 0.00
            longitude += 0.005
    }

Of course, if this was a real application the infos would came from a web socket-like architecture. Again, this is only an example.

And we need to animate the updateDeliveryPersonPosition:

    func updateDeliveryPersonPosition(deliveryPersonLatitude: Double, deliveryPersonLongitude: Double) {

        UIView.animate(withDuration: 0.1) { [self] in
            deliveryPersonAnnotation.coordinate = CLLocationCoordinate2D(latitude: deliveryPersonLatitude, longitude: deliveryPersonLongitude)
            mapView.showAnnotations([deliveryAddressAnnotation,deliveryPersonAnnotation], animated: true)
        }
    }

And it's done!

Conclusion

If you want to automatically zoom any number of map annotations, you should use the showAnnoattions(_,animated:) func . And to animate the changes, just animate the new coordinates and the zoom of it.

I hope you all enjoy this and if you have any comment please share below. I want to know what do you think about it and most important what I can improve here.

If you want really good books about general programming just go to the support section, where you can find the most useful books for developers that I read and you will help me buying from those links.

Thanks for the reading and... That's all folks.

credits: image

No Comments Yet