Newbie Questions
Newbie Questions

Reputation: 463

Searching Annotations in MapView

I followed a how-to-search-for-location-using-apples-mapkit about searching annotations in mapView and its searching around the world with MKLocalSearch.

However, I don't want to to search with MKLocalSearch but search my own annotations i added myself like these for example:

let LitzmanLocation = CLLocationCoordinate2DMake(32.100668,34.775192)
        // Drop a pin
        let Litzman = MKPointAnnotation()
        Litzman.coordinate = LitzmanLocation
        Litzman.title = "Litzman Bar"
        Litzman.subtitle = "נמל תל אביב 18,תל אביב"
        mapView.addAnnotation(Litzman)

        let ShalvataLocation = CLLocationCoordinate2DMake(32.101145,34.775163)
        // Drop a pin
        let Shalvata = MKPointAnnotation()
        Shalvata.coordinate = ShalvataLocation
        Shalvata.title = "Shalvata"
        Shalvata.subtitle = "האנגר 28,נמל תל אביב"
        mapView.addAnnotation(Shalvata)


        let MarkidLocation = CLLocationCoordinate2DMake(32.074961,34.781679)
        // Drop a pin
        let Markid = MKPointAnnotation()
        Markid.coordinate = MarkidLocation
        Markid.title = "Markid"
        Markid.subtitle = "אבן גבירול 30,תל אביב"
        mapView.addAnnotation(Markid)

Here is my Code:

MapViewController.Swift:

import UIKit
import MapKit
import CoreLocation

protocol HandleMapSearch {
    func dropPinZoomIn(placemark:MKPlacemark)
}

class MapViewController: UIViewController,MKMapViewDelegate, CLLocationManagerDelegate,UISearchBarDelegate{


    @IBOutlet var mapView: MKMapView!

    var resultSearchController:UISearchController? = nil

    var selectedPin:MKPlacemark? = nil

    @IBAction func MapSearchController(sender: AnyObject) {
        resultSearchController!.hidesNavigationBarDuringPresentation = false
        self.resultSearchController!.searchBar.delegate = self
        presentViewController(resultSearchController!, animated: true, completion: nil)
        self.resultSearchController!.searchBar.barTintColor = UIColor.blackColor()
        self.resultSearchController!.searchBar.placeholder = "חפש ברים"
        self.resultSearchController!.dimsBackgroundDuringPresentation = true
        self.resultSearchController!.searchBar.sizeToFit()
    }
override func viewDidLoad() {
        super.viewDidLoad()

        let locationSearchTable = storyboard!.instantiateViewControllerWithIdentifier("LocationSearchTable") as! LocationSearchTable
        resultSearchController = UISearchController(searchResultsController: locationSearchTable)
        resultSearchController?.searchResultsUpdater = locationSearchTable

        locationSearchTable.mapView = mapView

        locationSearchTable.handleMapSearchDelegate = self
}
}
}

extension MapViewController: HandleMapSearch {
    func dropPinZoomIn(placemark:MKPlacemark){
        // cache the pin
        selectedPin = placemark
        // clear existing pins
        mapView.removeAnnotations(mapView.annotations)
        let annotation = MKPointAnnotation()
        annotation.coordinate = placemark.coordinate
        annotation.title = placemark.name
        if let city = placemark.locality,
            let state = placemark.administrativeArea {
            annotation.subtitle = "(city) (state)"
        }
        mapView.addAnnotation(annotation)
        let span = MKCoordinateSpanMake(0.05, 0.05)
        let region = MKCoordinateRegionMake(placemark.coordinate, span)
        mapView.setRegion(region, animated: true)
    }
}

LocalSearchTable.Swift:

import UIKit
import MapKit

class LocationSearchTable : UITableViewController {

    var matchingItems:[MKMapItem] = []
    var mapView: MKMapView? = nil

    var handleMapSearchDelegate:HandleMapSearch? = nil

    func parseAddress(selectedItem:MKPlacemark) -> String {
        // put a space between "4" and "Melrose Place"
        let firstSpace = (selectedItem.subThoroughfare != nil && selectedItem.thoroughfare != nil) ? " " : ""
        // put a comma between street and city/state
        let comma = (selectedItem.subThoroughfare != nil || selectedItem.thoroughfare != nil) && (selectedItem.subAdministrativeArea != nil || selectedItem.administrativeArea != nil) ? ", " : ""
        // put a space between "Washington" and "DC"
        let secondSpace = (selectedItem.subAdministrativeArea != nil && selectedItem.administrativeArea != nil) ? " " : ""
        let addressLine = String(
            format:"%@%@%@%@%@%@%@",
            // street number
            selectedItem.subThoroughfare ?? "",
            firstSpace,
            // street name
            selectedItem.thoroughfare ?? "",
            comma,
            // city
            selectedItem.locality ?? "",
            secondSpace,
            // state
            selectedItem.administrativeArea ?? ""
        )
        return addressLine
    }

    }

extension LocationSearchTable : UISearchResultsUpdating {
    func updateSearchResultsForSearchController(searchController: UISearchController) {
        guard let mapView = mapView,
            let searchBarText = searchController.searchBar.text else { return }
        let request = MKLocalSearchRequest()
        request.naturalLanguageQuery = searchBarText
        request.region = mapView.region
        let search = MKLocalSearch(request: request)
        search.startWithCompletionHandler { response, _ in
            guard let response = response else {
                return
            }
            self.matchingItems = response.mapItems
            self.tableView.reloadData()
        }
    }

}

extension LocationSearchTable {
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return matchingItems.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("MapSearchCell")!
        let selectedItem = matchingItems[indexPath.row].placemark
        cell.textLabel?.text = selectedItem.name
        cell.detailTextLabel?.text = parseAddress(selectedItem)
        return cell
    }
}

extension LocationSearchTable {
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let selectedItem = matchingItems[indexPath.row].placemark
        handleMapSearchDelegate?.dropPinZoomIn(selectedItem)
        dismissViewControllerAnimated(true, completion: nil)
    }
}

Upvotes: 5

Views: 2241

Answers (2)

Purushothaman
Purushothaman

Reputation: 358

you just refer to this link http://www.coderzheaven.com/2016/02/14/mapkit-demo-swift-annotation-custom-annotation-custom-annotation-with-button-search-showing-directions-apple-maps-ios/

you will understand the following topics

  • Show Annotation in Maps.
  • Show Custom Annotation in Maps.
  • Show Custom Annotation with Custom Button in Maps.

Upvotes: 2

Jakub
Jakub

Reputation: 13860

TL;DR

Example project: https://github.com/JakubMazur/SO40539590


Ok, so for the beginning I will suggest you separate data from your controller code. I choose json format as most universal one. So:

[
   {
      "title":"Litzman Bar",
      "subtitle":"נמל תל אביב 18,תל אביב",
      "coordinates":{
         "lat":32.100668,
         "lon":34.775192
      }
   },
   {
      "title":"Shalvata",
      "subtitle":"האנגר 28,נמל תל אביב",
      "coordinates":{
         "lat":32.101145,
         "lon":34.775163
      }
   },
   {
      "title":"Markid",
      "subtitle":"אבן גבירול 30,תל אביב",
      "coordinates":{
         "lat":32.074961,
         "lon":34.781679
      }
   }
]

This is basically your database.

Now let's parse it into an Array to use inside your ViewConttroller. Again I will suggest you split it into Model objects like Location and Coordinate. Let's have a look in one class of it as example:

class Location: NSObject {

    var title : String = String()
    var subtitle : String = String()
    var coordinates : Coordinate = Coordinate()

    public class func locationFromDictionary(_ dictionary : Dictionary<String, AnyObject>) -> Location {
        let location : Location = Location()
        location.title = dictionary["title"] as! String
        location.subtitle = dictionary["subtitle"] as! String
        location.coordinates = Coordinate.coordinateFromDictionary(dictionary["coordinates"] as! Dictionary<String, AnyObject>)
        return location;
    }
}

I will not paste the code for parsing json file to this objects, because that's what is not this question is about. You will find in into repository.

And now let's focus on question.

I will recommend you not to search annotations, but search your data model and redraw annotations when needed

In order to do that (i will use UISearchBar for it):

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
    self.displayArray = self.locationsDatabase.filter() {
        return $0.title.contains("i")
    }

Then when you ovverride a setter like this:

var displayArray : Array<Location> = [] {
    didSet {
        self.mapView .removeAnnotations(self.mapView.annotations)
        for location in displayArray {
            let coords : Coordinate = location.coordinates
            let point = MKPointAnnotation()
            point.coordinate = CLLocationCoordinate2DMake(CLLocationDegrees(coords.latitude),CLLocationDegrees(coords.longitude))
            point.title = location.title
            point.subtitle = location.subtitle
            mapView.addAnnotation(point)
        }
    }
}

You can redraw your annotations. The edge cases like empty search field, case sensivity, dismissing keyboard I leave to you. But hope you will get the general idea. You may think that it's overengineered but having objects as a separate classes and universal format as an data input may benefit in the future.

Full project: https://github.com/JakubMazur/SO40539590

Upvotes: 0

Related Questions