Tip: Tap A Map

Most map applications let you select a point on the map. However, touching a map usually is only for moving a map around. You might want to get a coordinate at a user’s tap. In this tip, I’ll show you how to get the coordinate, and place a marker there.
Download the starter file and you’ll find an application for displaying a map of Twin Rivers Wisconsin. This is one of three contenders, the others being Evanston IL and Ithaca NY, for the first Ice Cream Sundae. Run the code and you get a map.

Stop the app. There’s a few steps for converting a touch into a coordinate. First you need tap, and react to the tap. Second, you get the coordinate of the tap in the system of the view. Finally you get a map coordinate that corresponds to the view’s coordinate.

To get the touch you use tap gesture recognizer, go to the storyboard, and type gesture into the finder.

You see several gestures. Some of these are continuous, like pinches, and some, like the tap, are discrete. Drag a tap gesture recognizer to the scene, making sure you release over the MKMapView object.  You’ll see it does not become part of the view, but the view controller.

You can control the nature of the tap in the attributes. A double tap on a map is a map zoom gesture, so keep the default single tap and touch.

Close up the attributes inspector. Open the Assistant editor and make a action from the gesture recognizer to ViewController. I’ll call it tapGesture.

Close the Assistant editor. Switch over to ViewController.swift and find the tapGesture method. Gesture recognizers fire for all kinds of reasons, so you have to pick the one you do your action. Type this:

if sender.state == .

You’ll see states that a tap can be.

For taps, you only care about when your finger leaves the device, since your finger might be moving before that. Select .ended to make the best location for the tap.

if sender.state == .ended{
}

I’ve got a tap. I’ll find the location of the touch on a view with the gesture recognizer’s locationInview method.

let locationInView = sender.location(in: mapView)

That requires a view. You have two choices here. You can use the sender’s view, which will be the entire tappable area, or the mapView. I’ll use the mapView.

Location returns a CGPoint in the view.
I’ll make that CGPoint into a map coordinate of type CLLocationCoordinate2d. There’s another method for that. MapKit includes a method convert. Add this:

let tappedCoordinate = mapView.convert(locationInView , toCoordinateFrom: mapView)

convert has two parameters. The first is the CGPoint of the tapped point. The second is the view to use for the coordinate system. Generally this will be your map. I also match this to the view in the location method. You get the most accurate conversion that way.
I get a CLCoordinateLocation2D from this which matches the tapped coordinate. With that coordinate, I’ll make an annotation with a method I already created.

addAnnotation(coordinate: tappedCoordinate)

Set your simulator for an iPhone 8 plus. Build and run this. Tap on the map and a marker appears. Tap again and another marker appears.

Tap outside the view and nothing happens. Clear removes the annotations.
Getting taps to map coordinates takes a few steps, but is not difficult. Your biggest challenge is to make sure you use the right views for getting the tap and the map coordinates.

This was a text version of the iOS Development Tips Weekly video series you can find at the Lynda.com and LinkedIn Learning libraries. Click the image at left  to view.

The Whole Code

You’ll find the code below. You can also download the code from GitHub

//
//  ViewController.swift
//  MapTouch
//
//  Created by Steven Lipton on 2/15/18.
//  Copyright © 2018 Steven Lipton. All rights reserved.
//

import UIKit
import MapKit

class ViewController: UIViewController,MKMapViewDelegate {
    //Two Rivers WI, the likely home of the Ice cream sundae
    let baseCoordinate = CLLocationCoordinate2DMake(44.1487225,-87.5684114)
    
    @IBOutlet weak var mapView: MKMapView!
    
    @IBAction func clearAnnotations(_ sender: UIButton) {
        mapView.removeAnnotations(mapView.annotations)
    }
    
    @IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
        if sender.state == .ended{
            let locationInView = sender.location(in: mapView)
            let tappedCoordinate = mapView.convert(locationInView, toCoordinateFrom: mapView)
            addAnnotation(coordinate: tappedCoordinate)
        }
    }
    func addAnnotation(coordinate:CLLocationCoordinate2D){
        let annotation = MKPointAnnotation()
        annotation.coordinate = coordinate
        mapView.addAnnotation(annotation)
    }
    func setupMap(){
        let region = MKCoordinateRegionMakeWithDistance(baseCoordinate, 500, 500)
        mapView.region = region
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        setupMap()
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.