Newbie Questions
Newbie Questions

Reputation: 463

Implementing search to map view controller

In my project i have a mapView with a lot of annotations & i would like to add a search functionality to the map so i can search those annotations and quickly find the annotation i want.

I followed a tutorial i found on the web but it searches globally (MKLocalSearch) and not the annotations.

I tried looking for a tutorial \ Help for my problem but i couldn't get any help for a long time now. Here is my search code :

I've made these annotations :

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)

Currently the search i have:

MapViewController:

    //All my Map code is here
    }
    }
    }
    extension MapViewController: HandleMapSearch {
    func dropPinZoomIn(placemark:MKPlacemark){
        // cache the pin
        selectedPin = placemark
        // clear existing pins
        let annotation = MKPointAnnotation()
        annotation.coordinate = placemark.coordinate
        annotation.title = placemark.name
        if let _ = placemark.locality,
            let _ = placemark.administrativeArea {
            annotation.subtitle = ""
        }
        mapView.addAnnotation(annotation)
        let span = MKCoordinateSpanMake(0.01, 0.01)
        let region = MKCoordinateRegionMake(placemark.coordinate, span)
        mapView.setRegion(region, animated: true)
    }  
}

SearchTable:

import UIKit
import MapKit

class LocationSearchTable : UITableViewController {

    var matchingItems = [CustomAnnotations]()
    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
    }

    func search(keywords:String) {
        self.matchingItems.removeAll()
        for annotation in self.mapView!.annotations {
            if annotation.isKindOfClass(CustomAnnotations) {
                //Just an example here for searching annotation by title, you could add other filtering actions else.
                if (annotation.title??.rangeOfString(keywords) != nil) {
                    self.matchingItems.append(annotation as! CustomAnnotations)
                }
            }
        }
        self.tableView.reloadData()
    }

}

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", forIndexPath: indexPath)
        let selectedItem = matchingItems[indexPath.row]
        cell.textLabel?.text = selectedItem.title
        cell.detailTextLabel?.text = selectedItem.subtitle
        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)
    }
}

My question is how i can turn this to only search my annotations and not search all over the world with MKLocalSearch.

using swift 3 and Xcode 8

Thanks you for helping.

Upvotes: 0

Views: 597

Answers (1)

Mike G
Mike G

Reputation: 104

If I’m reading this correctly, you just want to search annotations rather than all MapKit items. You could update the code in updateSearchResults to search the mapView.annotations array and filter your results (matchingItems) based on what’s in the search controller’s searchbar.

func updateSearchResults(for searchController: UISearchController) {

    matchingItems = []
    guard let mapView = mapView,
        let searchBarText = searchController.searchBar.text else { return }

    for item in self.mapView!.annotations    {
        let title = item.title! as! String

        if title.hasPrefix(searchBarText) && searchBarText != ""  {
            matchingItems.append(item)
        }
    }
    self.tableView.reloadData()
}

I wrote a quick test app and posted it https://github.com/hellzkitchen/stackoverflow-041917. In my example, I passed the selected annotation to handleMapSearch delegate method and just updated the mapView to center on it. Hope this helps.

Upvotes: 3

Related Questions